一、前端项目结构
在上一节的基础上,我们分别在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