完善template:layout、router、store、util

This commit is contained in:
zhouxhere 2022-01-24 15:19:30 +08:00
parent f2e5e0913a
commit 2ca6889ed4
18 changed files with 265 additions and 66 deletions

View File

@ -5,7 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"serve": "webpack serve --open --config webpack.dev.js", "start": "webpack serve --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js", "build": "webpack --config webpack.prod.js",
"pre-commit": "lint-staged" "pre-commit": "lint-staged"
}, },

View File

@ -5,7 +5,7 @@
<link rel="icon" href="favicon.ico" /> <link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<link rel="manifest" href="manifest.json" /> <link rel="manifest" href="/manifest.json" />
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
</head> </head>
<body> <body>

8
src/App.js Normal file
View File

@ -0,0 +1,8 @@
import React from 'react'
import Router from './router'
const App = () => {
return <Router />
}
export default App

15
src/config/defaultApp.js Normal file
View File

@ -0,0 +1,15 @@
const { RouteCode } = require('./routeConstants')
module.exports = {
name: 'app',
urls: {
loginUrl: '/passport/login',
defaultUrl: '/home/index',
},
redirects: [
{
from: RouteCode.ROOT,
to: RouteCode.PAGE_HOME,
},
],
}

View File

@ -0,0 +1,11 @@
export const RoutePath = {
ROOT: 'layout',
LAYOUT_BASIC: 'layout/basic',
PAGE_HOME: 'page/home',
}
export const RouteCode = {
ROOT: 'LAYOUT',
LAYOUT_BASIC: 'LAYOUT_BASIC',
PAGE_HOME: 'PAGE_HOME',
}

View File

@ -3,13 +3,13 @@ import 'regenerator-runtime'
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import Router from './router' import App from './App'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import store from './store' import store from './store'
const element = ( const element = (
<Provider store={store}> <Provider store={store}>
<Router /> <App />
</Provider> </Provider>
) )

8
src/layout/index.js Normal file
View File

@ -0,0 +1,8 @@
import React from 'react'
import { Outlet } from 'react-router-dom'
const Layout = () => {
return <Outlet />
}
export default Layout

View File

@ -1,15 +1,47 @@
import React from 'react' import React, { lazy, Suspense } from 'react'
import { BrowserRouter, Route, Routes } from 'react-router-dom' import { useSelector } from 'react-redux'
import BasicLayout from '../layout/basic' import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'
import Home from '../page/home' import Redirect from './redirect'
import { defaultRoutes } from './routes'
import { routerTool } from '../util/router'
const Router = () => { const Router = () => {
const appUrls = useSelector((state) => state.app.urls)
const redirects = useSelector((state) => state.app.redirects)
const processRoutes = (params) => {
if (!params) return []
let allRedirects = routerTool.processRoutes(defaultRoutes, redirects)
let routes = params.map((item) => {
let Component = lazy(() => import(`src/${item.component}`))
let redirect = allRedirects.find((p) => p.from === item.code)
let props = {
element: (
<Suspense fallback={<div>loading</div>}>
<Redirect redirect={redirect ? redirect.toUrl : ''}>
<Component />
</Redirect>
</Suspense>
),
path: item.path,
}
return (
<Route key={item.path} {...props}>
{processRoutes(item.children)}
</Route>
)
})
return routes
}
return ( return (
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
<Route path='/' element={<BasicLayout />}> {processRoutes(defaultRoutes)}
<Route element={<Home />} index /> <Route
</Route> path='*'
element={<Navigate to={appUrls?.defaultUrl || '/'} />}
/>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
) )

20
src/router/redirect.js Normal file
View File

@ -0,0 +1,20 @@
/* eslint-disable react/prop-types */
import React, { useEffect } from 'react'
import { generatePath, useMatch, useNavigate } from 'react-router-dom'
const Redirect = (props) => {
const match = useMatch(props.redirect)
const navigate = useNavigate()
useEffect(() => {
if (props.redirect) {
if (match) {
navigate(generatePath(props.redirect, match.params))
} else {
navigate(props.redirect)
}
}
}, [])
return <>{props.children}</>
}
export default Redirect

26
src/router/routes.js Normal file
View File

@ -0,0 +1,26 @@
import { RouteCode, RoutePath } from '../config/routeConstants'
export const defaultRoutes = [
{
code: RouteCode.ROOT,
title: 'root',
path: '/',
component: RoutePath.ROOT,
children: [
{
code: RouteCode.LAYOUT_BASIC,
title: 'home',
path: 'home',
component: RoutePath.LAYOUT_BASIC,
children: [
{
code: RouteCode.PAGE_HOME,
title: 'index',
path: 'index',
component: RoutePath.PAGE_HOME,
},
],
},
],
},
]

View File

@ -2,11 +2,11 @@ import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk' import thunk from 'redux-thunk'
import storageEnhancer from './enhancer/storage' import storageEnhancer from './enhancer/storage'
import loggerMiddleware from './middleware/logger' import loggerMiddleware from './middleware/logger'
import appReducer from './reducer/app'
import countReducer from './reducer/count' import countReducer from './reducer/count'
import todoReducer from './reducer/todo'
const reducer = combineReducers({ const reducer = combineReducers({
todo: todoReducer, app: appReducer,
count: countReducer, count: countReducer,
}) })
const middlewareEnhancer = applyMiddleware(loggerMiddleware, thunk) const middlewareEnhancer = applyMiddleware(loggerMiddleware, thunk)

18
src/store/reducer/app.js Normal file
View File

@ -0,0 +1,18 @@
import defaultApp from '../../config/defaultApp'
import { copyTool } from '../../util/copy'
import { storeTool } from '../../util/store'
const fetch = async (state) => {
return state
}
const update = async (state, ...props) => {
return copyTool.merger(state, ...props)()
}
const appReducer = storeTool.createReducer(defaultApp, {
APP_FETCH: fetch,
APP_UPDATE: update,
})
export default appReducer

View File

@ -1,15 +1,15 @@
import { storeTool } from '../../util/store' import { storeTool } from '../../util/store'
const addCount = (countState) => { const addCount = (state) => {
return countState + 1 return state + 1
} }
const setCount = (countState, param) => { const setCount = (state, param) => {
return param return param
} }
const delCount = (countState) => { const delCount = (state) => {
return countState <= 0 ? 0 : countState - 1 return state <= 0 ? 0 : state - 1
} }
const countReducer = storeTool.createReducer(0, { const countReducer = storeTool.createReducer(0, {

View File

@ -1,30 +0,0 @@
import { storeTool } from '../../util/store'
const addTodo = (todoState, param) => {
const newTodo = todoState.concat(param)
return newTodo
}
const editTodo = (todoState, param) => {
const _index = todoState.findIndex((item) => item.id === param.id)
if (_index < 0) return newTodo
const newTodo = storeTool.updateItemInArray(todoState, _index, (item) => {
return storeTool.updateObject(item, param)
})
return newTodo
}
const delTodo = (todoState, param) => {
const _index = todoState.findIndex((item) => item.id === param.id)
if (_index < 0) return newTodo
const newTodo = todoState.splice(_index, 1)
return newTodo
}
const todoReducer = storeTool.createReducer([{ id: 1, name: 'hello' }], {
ADD_TODO: addTodo,
EDIT_TODO: editTodo,
DEL_TODO: delTodo,
})
export default todoReducer

74
src/util/copy.js Normal file
View File

@ -0,0 +1,74 @@
const defineType = (param) => {
let type = Object.prototype.toString.call(param)
return type.split(' ')[1].split(']')[0]
}
const mergeObject = (target = {}, source, isJoin, isReplace, isFilter) => {
for (const key in source) {
if (Object.hasOwnProperty.call(target, key)) {
if (isReplace) {
target[key] = source[key]
} else {
target[key] = merger(target[key], source[key])(
isJoin,
isReplace,
isFilter
)
}
} else {
if (isJoin) {
target[key] = merger(target[key], source[key])(
isJoin,
isReplace,
isFilter
)
}
}
}
return target
}
const mergeArray = (target = [], source, isJoin, isReplace, isFilter) => {
if (isJoin) {
target = [...target, ...source]
} else {
for (let i = 0; i < source.length; i++) {
if (isReplace) {
target[i] = source[i]
} else {
target[i] = merger((target && target[i]) || undefined, source[i])(
isJoin,
isReplace,
isFilter
)
}
}
}
if (isFilter) {
target = [...new Set(target)]
}
return target
}
const merger = (...props) => {
let res
const merge = (isJoin = true, isReplace = false, isFilter = true) => {
for (let i = 0; i < props.length; i++) {
switch (defineType(props[i])) {
case 'Array':
res = mergeArray(res, props[i], isJoin, isReplace, isFilter)
break
case 'Object':
res = mergeObject(res, props[i], isJoin, isReplace, isFilter)
break
default:
res = props[i]
break
}
}
return res
}
return merge
}
export const copyTool = { merger }

33
src/util/router.js Normal file
View File

@ -0,0 +1,33 @@
const findPath = (params, code) => {
for (let i = 0; i < params.length; i++) {
if (params[i].code === code) {
return [params[i].path]
} else {
if (params[i].children) {
let childPath = findPath(params[i].children, code)
return childPath ? [params[i].path, ...childPath] : []
}
return []
}
}
}
const processRoutes = (routes, redirects) => {
return redirects.map((item) => {
let fromUrl = findPath(routes, item.from)
if (fromUrl.length > 0) {
fromUrl = fromUrl.join('/').replace('//', '/')
}
let toUrl = findPath(routes, item.to)
if (toUrl.length > 0) {
toUrl = toUrl.join('/').replace('//', '/')
}
return {
...item,
fromUrl,
toUrl,
}
})
}
export const routerTool = { processRoutes }

View File

@ -1,20 +1,3 @@
const updateObject = (oldVal, newVal) => {
return Object.assign({}, oldVal, newVal)
}
const updateItemInArray = (array, index, updateCallback) => {
const updatedArray = array.map((_item, _index) => {
if (_index !== index) {
return _item
}
const updatedItem = updateCallback(_item)
return updatedItem
})
return updatedArray
}
const createReducer = (initialState, handlers) => { const createReducer = (initialState, handlers) => {
return function reducer(state = initialState, action) { return function reducer(state = initialState, action) {
if (Object.prototype.hasOwnProperty.call(handlers, action.type)) { if (Object.prototype.hasOwnProperty.call(handlers, action.type)) {
@ -25,4 +8,4 @@ const createReducer = (initialState, handlers) => {
} }
} }
export const storeTool = { createReducer, updateObject, updateItemInArray } export const storeTool = { createReducer }

View File

@ -1,3 +1,4 @@
const path = require('path')
const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin') const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin')
const { merge } = require('webpack-merge') const { merge } = require('webpack-merge')
const common = require('./webpack.common') const common = require('./webpack.common')