前言: 此文需要有一定 react,redux 基础, 具体学习资料请科学上网.
使用 create-react-app 脚手架
具体基础配置请参考
配合 antd 组件实现的管理系统 demo, 线上地址 http://www.react.doudoujs.com/#/login
开发前反思
1. 按需加载
webpack 的 import 动态加载的模块的函数, import(参数), 参数为模块地址.
注意: import 后会返回一个 promise 对象.
- import('/components/chart').then(mud => {
- dosomething(mod)
- });
本 demo 构建了异步加载组件 Bundle, 具体代码请见
- class Bundle extends Component {
- constructor(props) {
- super(props);
- this.state = {
- mod: null
- };
- }
- unmount = false
- componentDidMount = () => {
- // 加载组件时, 打开全局 loading
- this.props.dispatch(loading(true))
- this.load(this.props)
- }
- componentWillUnmount = () => {
- this.unmount = true
- }
- componentWillReceiveProps(nextProps) {
- if (nextProps.load !== this.props.load) {
- this.load(nextProps)
- }
- }
- load(props) {
- if (this.state.mod) {
- return true
- }
- // 注意这里, 使用 Promise 对象; mod.default 导出默认
- props.load().then((mod) => {
- if (this.unmount) {
- // 离开组件时, 不异步执行 setState
- this.props.dispatch(loading(false))
- return false
- }
- this.setState({
- mod: mod.default ? mod.default : mod
- }, _ => {
- // 组件加载完毕, 关闭 loading
- this.props.dispatch(loading(false))
- });
- });
- }
- render() {
- return this.state.mod ? this.props.children(this.state.mod) : null;
- }
- }
具体使用
- <Bundle load={() => import('路径')}>
- {Comp => {
- return Comp ? <Comp /> : <div > 加载中...</div>
- }}
- </Bundle>
2. 全局 loading
配合 redux,dispatch => reducer 更新 => mapstate 更新, 在根组件进行 loading 的渲染
详细请见本 demo 地址 src/routers/router.js--render 函数
3. 配置路由对象
项目布局如下
本 demo 使用的是 router4, 官方文档演示为单行 Route(如 vue 种的 router), 未有统一配置对象. 管理系统基本围绕着 content 进行业务开发, 构建通用配置有助于开发 构建 router.config.js
- const routers = [
- {
- menuName: '主页',
- menuIco: 'home',
- component: 'home/home.js', // 主页
- path: '/admin/home' // 主页
- },
- {
- menuName: '用户',
- menuIco: 'user',
- children: [
- {
- menuName: '用户列表',
- component: 'user/list.js', // 主页
- path: '/admin/user/list' // 主页
- }
- ]
- },
- {
- menuName: '多级菜单',
- menuIco: 'setting',
- children: [
- {
- menuName: '多级菜单 2',
- children: [
- {
- menuName: '菜单',
- component: 'user/list.js', // 主页
- path: '/admin/user/list3' // 主页
- }
- ]
- }
- ]
- },
- {
- menuName: '关于我',
- menuIco: 'smile-o',
- component: 'about/about.js', // 主页
- path: '/admin/about' // 主页
- }
- ]
实现思路, 最外层布局为 Admin,content 被 Admin 包裹, 那么可以利用 this.props.children , 把内容打入 content 中.(利用 bundle 组件异步加载后塞入组件进行渲染)
- <Admin>
- <Content { ...this.props } breadcrumb={this.state.breadcrumb}>
- {this.props.children}
- </Content>
- </Admin>
- // Content 组件内部
- render() {
- return (
- <div>
- {this.props.children}
- </div>
- )
- }
- // 本 demo 实现, 详见 src/routers/router.js
- <Route
- path="/admin"
- render={item => (
- <Admin {...item} { ...this.props }>
- {initRouters.map(el => this.deepItem(el, { ...this.props, ...item}))}
- </Admin>
- )}
- />
4. 配置通用 reducer
多人配合开发, 一些业务场景的组件需要状提升 (不理解状态提升的同学, 请科学上网)
- import otherReducers from './otherReducers'
- const App = combineReducers({
- rootReducers,
- ...otherReducers // 其他需要增加的 reducers
- })
5. 登陆验证
利用 withRouter 函数, 页面进行路由跳转时触发该函数
- const newWithRouter = withRouter(props => {
- // ....
- })
若未登录, 则返回
return <Redirect to="/login" />
6. 路由拦截
同上, 根据路由配置与权限, 返回相应的菜单或屏蔽
return <Redirect to={其他} />
7 其他配置
7-1. 自定义样式
- // 修改 webpack.config.dev.js 和 webpack.config-prod.js 配置文件
- {
- test: /\.(CSS|less)$/,
- // 匹配 src 的都自动加载 css-module
- include: [/src/],
- exclude: [/theme/],
- use: [
- require.resolve('style-loader'), {
- loader: require.resolve('css-loader'),
- options: {
- importLoaders: 1,
- modules: true, // 新增对 css modules 的支持
- localIdentName: '[path]___[name]__[local]___[hash:base64:5]'
- }
- }, {
- loader: require.resolve('postcss-loader'),
- options: {
- // Necessary for external CSS imports to work
- // https://github.com/facebookincubator/create-react-app/issues/2677
- ident: 'postcss',
- plugins: () => [
- require('postcss-flexbugs-fixes'),
- autoprefixer({
- browsers: [
- '>1%', 'last 4 versions', 'Firefox ESR', 'not ie <9', // React doesn't support IE8 anyway
- ],
- flexbox: 'no-2009'
- })
- ]
- }
- }, {
- loader: require.resolve('less-loader') // compiles Less to CSS
- }
- ]
- }, {
- // 不匹配 node_modules,theme 的都不能自动加载 css-module
- test: /\.(css|less)$/,
- include: [/node_modules/,/theme/],
- use: [
- {
- loader: "style-loader"
- }, {
- loader: "css-loader",
- options: {
- importLoaders: 1
- }
- }, {
- loader: require.resolve('less-loader') // compiles Less to CSS
- }
- ]
- },
使用: 在 App.js 中直接导入
import './assets/theme/App.less'
7-2. 热更新
步骤一:
- // 安装 react-hot-loader
- npm install --save-dev react-hot-loader
步骤二:
在 webpack.config.js 的 entry 值里加上 react-hot-loader/patch
步骤三:
webpackDevServer.config.js 中 hot 设置为 true
步骤四: 在 webpack.config.dev.js 中在 babel-loader 中 plugins 加入 react-hot-loader/babel
- {
- test: /\.(js|jsx|mjs)$/,
- include: paths.appSrc,
- loader: require.resolve('babel-loader'),
- options: {
- // This is a feature of `babel-loader` for webpack (not Babel itself). It
- // enables caching results in ./node_modules/.cache/babel-loader/ directory for
- // faster rebuilds.
- cacheDirectory: true,
- plugins: [
- 'react-hot-loader/babel'
- ]
- }
- },
步骤五:
重写 index.js,App 挂载
- import { AppContainer } from 'react-hot-loader'
- const render = Component => {
- ReactDOM.render(
- <AppContainer>
- <Component></Component>
- </AppContainer>,
- document.getElementById('root')
- )
- }
- render(App)
- if(module.hot) {
- module.hot.accept('./App',() => {
- render(App);
- });
- }
7-3. 本地浏览
直接在 package.json 中 加入
homepage:'.'
后记: 使用 react 与 vue 的感悟
react 是函数式编程, 代码难度, 学习曲度, 装逼指数, 社区生态多样性相比 vue 更高一点.
vue 提供了大量的指令降低了开发难度, 详细完善的文档, 上手更快.
react 提供较少的 api, 相比 vue 的指令, 业务场景的功能需要自己实现, 难度更高一点
vue 适合中小型项目, 单兵, 少量人员配合快速开发
react 适合大型项目, 多人协作
来源: http://www.jb51.net/article/138807.htm