一、前端项目结构
在上一节的基础上,我们分别在src下创建如下文件夹:
- assets:静态文件;
- components:公共组件,比如面包屑、编辑器、svg图标、分页器等等;
- hooks:函数组件,使用 React 16.8引进的Hook 特性实现;
- layout:布局组件;
- redux:redux目录,负责状态管理;
- routes:路由,负责路由管理;
- styles:全局样式;
- utils:工具包;
- views:视图层;
二、redux目录构建
我们项目使用redux进行状态管理,在使用redux状态管理器之前,我们需要安装依赖包:
npm install redux --save npm install react-redux --save npm install redux-logger --save npm install redux-thunk --save npm install redux-devtools-extension --save
1、在redux文件夹下创建root_reducers.js文件,用于保存整个项目使用到的reducer:
/** * @author zy * @date 2020/4/5 * @Description: 合并reducer */ import {combineReducers} from 'redux'; export default combineReducers({})
这里利用 combineReducers 函数来把多个 reducer 函数合并成一个 reducer 函数,目前还没有引入redux函数,后面我们会逐渐完善。
2、在redux文件夹下创建Index.js文件:
/** * @author zy * @date 2020/4/4 * @Description: redux状态管理器配置 * 不懂原理的可以参考:https://github.com/brickspert/blog/issues/22#middleware */ import thunk from 'redux-thunk'; import {compose, createStore, applyMiddleware} from 'redux'; import rootReducers from './root_reducers'; import {composeWithDevTools} from 'redux-devtools-extension'; const storeEnhancers = process.env.NODE_ENV === 'production' ? compose(applyMiddleware(thunk)) : compose()(composeWithDevTools(applyMiddleware(thunk))); /** * 创建store * @author zy * @date 2020/4/5 */ const configureStore = () => { //创建store对象 const store = createStore(rootReducers, storeEnhancers); //reducer热加载 if (process.env.NODE_ENV !== 'production') { if (module.hot) { module.hot.accept('./root_reducers', () => { store.replaceReducer(rootReducers) }) } } return store; } export default configureStore();
这里我们利用createStore创建了一个状态管理器,并传入了redux,此外我们还使用了thunk中间件来处理异步请求。
如果不理解这部分代码,可以先去看一下redux相关知识:
三、routes目录构建
路由构建是使用React Route路由库实现的,在使用之前,我们需要安装以下依赖:
npm install react-router-dom --save
1、在routes文件夹下创建web.js文件:
/** * @author zy * @date 2020/4/5 * @Description: web路由 * 不懂的可以参考:https://segmentfault.com/a/1190000020812860 * https://reacttraining.com/react-router/web/api/Route */ import React from 'react'; import PageNotFound from '@/components/404'; function Home(props) { console.log('Home=>', props); return <h2>Home</h2> } function About(props) { console.log('About=>', props); return <h2>About</h2>; } /** * web路由配置项 * @author zy * @date 2020/4/5 */ export default { path: '/', name: 'home', component: Home, childRoutes: [ {path: 'about', component: About}, {path: '*', component: PageNotFound} ] }
可以看到我们最后导出了web路由配置项,访问/会加载Home组件,访问/about会加载About组件,访问其它页面会返回页面找不到组件。
2、在routes下创建index.js文件:
import React from 'react'; import {Switch, Route} from 'react-router-dom'; import _ from 'lodash'; import webRouteConfig from './web'; //保存所有路由配置的数组 const routeConfig = [webRouteConfig] /** * 路由配置 * @author zy * @date 2020/4/5 */ export default function () { /** * 生成路由嵌套结构 * @author: zy * @date: 2020-03-05 * @param routeConfig: 路由配置数组 * @param contextPath: 路由根路径 */ const renderRouters = (routeConfig, contextPath = '/') => { const routes = []; const renderRoute = (item, routeContextPath) => { //基路径 let path = item.path ? `${contextPath}/${item.path}` : contextPath path = path.replace(/\/+/g, '/') if (!item.component) { return; } //当前路由 routes.push( <Route key={path} path={path} component={item.component} exact /> ); //子路由 if (item.childRoutes) { _.forEach(item.childRoutes, item => { renderRoute(item, path); }) } }; _.forEach(routeConfig, item => renderRoute(item, contextPath)) return <Switch>{routes}</Switch> }; return renderRouters(routeConfig); }
如果不理解这部分代码,可以先去看一下react router相关知识:
四、components目录构建
在web.js中我们使用到了PageNotFound组件,我们需要在components下创建404文件,并在该文件夹下创建index.jsx文件,代码如下:
/** * @author zy * @date 2020/4/5 * @Description: 找不到页面 */ import React from 'react'; import {Result, Button} from 'antd'; /** * 页面找不到组件 * @author zy * @date 2020/4/5 */ function PageNotFound(props) { return ( <Result status='404' title='404' subTitle='Sorry, the page you visited does not exist.' extra={ <Button type='primary' onClick={() => { props.history.push('/') }}> Back Home </Button> } /> ) } export default PageNotFound
由于此处我们使用了antd组件,因此需要引入依赖:
cnpm install antd --save
关于更多antd组件的使用请查看:antd官网。
五、hooks目录构建
我们在hooks文件夹下创建use_bus.js文件,该文件用于构建事件监听器,后面我们用到会具体介绍:
/** * @author zy * @date 2020/4/5 * @Description: 事件监听器 * useContext Hook 是如何工作的:https://segmentfault.com/a/1190000020111320?utm_source=tag-newest * useEffect Hook 是如何工作的:https://segmentfault.com/a/1190000020104281 * 微型库解读之200byte的EventEmitter - Mitt:https://segmentfault.com/a/1190000012997458?utm_source=tag-newest */ import React from 'react'; import mitt from 'mitt'; //创建上下文 const context = React.createContext(); //外层提供数据的组件 const Provider = context.Provider; //useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值 export function useBus() { return React.useContext(context); } /** * 事件监听器组件 * @author zy * @date 2020/4/5 * @param name:监听的事件名称 * @param fn:事件发布时的响应函数 */ export function BusListener(name, fn) { //获取 context 的当前值 const bus = useBus(); //组件第一次挂载执行,第二个参数发生变化时执行 React.useEffect(() => { //事件定阅 bus.on(name, fn); //组件卸载之前执行 return () => { //取消事件定阅 bus.off(name, fn); } }, [bus, name, fn]) } //外层提供数据的组件 向后代组件跨层级传值bus,这样后代组件都可以通过useBus获取到bus的值 export function BusProvider({children}) { const [bus] = React.useState(() => mitt()); return <Provider value={bus}>{children}</Provider> }
这里使用到了React 16.8引进的Hook新特性,感兴趣可以查看以下博客:
[3]微型库解读之200byte的EventEmitter - Mitt
六、App.js文件
我们修改App.js文件代码如下:
/** * @author zy * @date 2020/4/5 * @Description: 根组件 */ import React from 'react'; import Routes from '@/routes'; import {BrowserRouter} from 'react-router-dom'; export default function App(props) { return ( <BrowserRouter> <Routes/> </BrowserRouter> ) }
七、index.js文件
我们修改index.js文件如下:
/** * @author zy * @date 2020/4/5 * @Description: 入口文件 */ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import {AppContainer} from 'react-hot-loader'; import {BusProvider} from '@/hooks/use_bus'; import {Provider} from 'react-redux'; import store from '@/redux'; ReactDOM.render( <AppContainer> <BusProvider> <Provider store={store}> <App/> </Provider> </BusProvider> </AppContainer>, document.getElementById('root') )
这里我们引入了局部热更新,这样当我们修改部门文件时,不会造成整个页面的刷新,可以保留状态值。
npm install react-hot-loader --save
此外,我们还引入了状态管理器store,用来管理我们所有组件的状态。
在import文件的时候,我们引入了@别名,@指的的是src路径,其配置在webpack.config.js文件中:
至此,我们整个前端框架搭建完毕,我们可以运行程序,访问http://localhost:3000/:
此外,我们还可以访问about页面:
参考文章:
[5]antd官方手册
[8]微型库解读之200byte的EventEmitter - Mitt
来源:https://www.cnblogs.com/zyly/p/12632556.html