前沿
由于接触vue较多,工作中也是用vue做的后台管理,最近闲下来了,就想着学习下React,在这里记录下在学习过程中的遇到的问题,以及笔记。
- 项目中也参考的github中的项目react-admin再次表示大大的感谢😄
- 项目中的mock数据是借用了 easy-mock (由于有时候请求不稳定也会换)fastmock。
- 在线体验地址 https://react.closeeyes.cn
- github完整代码 🐛
1. 目录结构
由于也是刚接触react就直接使用create-react-app官方的脚手架了,由于需要做一些自定义配置直接yarn eject把webpack相关配置暴露出来。整理后的目录结构如下图:

在这里就简单介绍下目录结构
- build: 打包之后的代码存在的目录
- config: webpack 相关配置
- scripts: webpack 打包以及开发启动目录
- CNAME: 由于打包后的文件是- GitHub Pages绑定来自阿里云的域名,在这里配置下
- deploy.sh: 打包后的文件发布到- GitHub Pages相关操作
src目录是我们的源代码所处的位置
- containers: 页面所处的位置,也就是后台管理的内容区域。
- services: 与- ajax相关配置
2. 路由拦截与路由的懒加载
我们做后台管理有时候想要在进入页面之前做点什么,这个时候就会用到路由拦截,react-router-dom没有像Vue-router那样的beforeRouter生命周期的钩子函数,那就需要我们自己来做这件事情了,尝试了很多办法,总算是把这个事情解决了,先看代码: 
// react-loadable 是一个动态导入加载组件的高阶组件.
// 一个组件是import的组件,另一个是渲染组件  当用户进入页面是 加载异步组件,页面中显示的只是 loading....
import React from 'react'
import loadable from 'react-loadable'
function asyncImport(loader) {
  function Loading(props) {
    if (props.error)
      return <div> Error! </div>
    else if (props.pastDelay)
      return (
        <div>
          正在加载组件数据...
        </div>
      )
    else
      return null
  }
  return loadable({
    loader,
    loading: Loading,
  })
}
export {
  asyncImport
}
 
import { asyncImport } from '../utils/routerLoadable'
// 这里习惯性的使用 定义vue-router 的规则
const routes = [
  {
    name: 'Home',
    path: '/app',
    component: asyncImport(() => import('../containers/Home')),
    meta: {
      title: '首页',
      rules: ['loginRequired']
    }
  },
  {
    name: 'Home1',
    path: '/app1',
    component: asyncImport(() => import('../containers/Home1')),
    meta: {
      title: '首页1',
      rules: []
    }
  },
  // .......
]
export default routes 
import React from 'react'
import { Route, Switch, Redirect } from 'react-router-dom'
import routes from './api'
import queryString from 'query-string'
// 权限限制规则
const requiredRules = {
  /** 
   * 判断是否登录
   * @return true 条件满足 通过权限验证
   */
  loginRequired (path) {
    const localStorage = window.localStorage
    const strReactAdminUserInfo = localStorage.getItem('react-admin_user') || '{}'
    const reactAdminUserInfo = JSON.parse(strReactAdminUserInfo)
    return !!reactAdminUserInfo.userName || '/login'
  }
}
/**
 * @param  {Protected:登陆拦截(函数组建)}
 * @return {还是一个Route组件,这个Route组建使用的是Route三大渲染方式(component、render、children)的render方式}
 */
const Protected =  ({component: Comp, ...rest}) => {
  const { exact, path, meta, setTagPage, ...otherRest } = rest
  return (
    <Route {...rest} render={ () => {
      const { title } = meta
      document.title = title || 'react-admin'
      // 路由拦截 进入页面前 检查 逐个调用 meta里面的 rules 的规则. 
      // eg: /app 路由 rules是 ['loginRequired'], 存在登录验证规则, 进入页面之前会验证是否登录
      if (meta.rules && meta.rules instanceof Array) {
        const middlewares = meta.rules.map(item => requiredRules[item])
        for (let i = 0; i < middlewares.length; i++) {
          const result = middlewares[i](path)
          if (result) {
            return <Redirect to={result}/>
          }
        }
      }
      return <Comp {...otherRest}/>
    }}/>
  )
}
const routerApp = (props) => {
  const query = queryString.parse(props.location.search)
  props.match.query = query
  return (
    <Switch>
      {
        routes.map((item) => (
          <Protected
            {...props}
            path={ item.path }
            component={ item.component }
            key={ item.path }
            exact
            meta={item.meta}>
          </Protected>
        ))
      }
      <Route render={() => <Redirect to='/404'/>} />
    </Switch>
  )
}
export default routerApp 3. axios 请求拦截
// LoadingBar 是自己写的一个React组件
import axios from 'axios'
import LoadingBar from '@/components/LoadingBar'
let apiBaseURL = 'https://www.easy-mock.com/mock/5d088415bdc26d23199ba01a'
// const apiBaseURL = 'https://www.fastmock.site/mock/4dcea17ec42f04835302140b4dadeacc'
const instance = axios.create({
  baseURL: apiBaseURL,
  timeout: 5000
})
// request
instance.interceptors.request.use((config) => {
  LoadingBar.start()
  return config
}, (err) => {
  LoadingBar.error()
  return Promise.reject(err)
})
// response
instance.interceptors.response.use((response) => {
  LoadingBar.finish()
  return response.data
}, (err) => {
  LoadingBar.error()
  return Promise.reject(err)
})
export default instance
 4. redux
详细请跳转 传送门
5. css 模块化 类似与Vue的 scope
 reactjs没有像vue style scoped那样简单的配置就能实现css 模块化了,在这里是借助了第三方的插件 babel-plugin-react-css-modules 实现css 模块化。
配置过程中也遇到了一些坑~
- create-react-app在创建项目的时候已经做了模块化处理,默认匹配- *.module.(less|sass|css)的文件做模块化处理- const cssRegex = /\.css$/; const cssModuleRegex = /\.module\.css$/; const sassRegex = /\.(scss|sass)$/; const sassModuleRegex = /\.module\.(scss|sass)$/; const lessRegex = /\.(less)$/; const lessModuleRegex = /\.module\.(less)$/; 如果不做一些处理的话使用方法, 这样写- className只能写驼峰,书写麻烦
import React from 'react'
import cssModule from './index.module.less'
const CssModules = (props) => {
  return (
    
)
}
export default CssModules
- 配置 `babel-plugin-react-css-modules` ```bash yarn add babel-plugin-react-css-modules postcss-less -D
- webpack配置{ test: lessModuleRegex, use: getStyleLoaders({ importLoaders: 2, modules: true, // getLocalIdent: getCSSModuleLocalIdent, localIdentName: '[path][name]__[local]', sourceMap: isEnvProduction && shouldUseSourceMap, }, 'less-loader' ), },
- .babelrc.js
module.exports = {
  "presets": [
    "react-app"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    [
      "react-css-modules",
      {
        "exclude": 'node_modules',
        "generateScopedName": '[path][name]__[local]',
        "filetypes": {
          ".less": {
            "syntax": "postcss-less"
          }
        }
      }
    ]
  ]
} - css名字的规则 [path][name]__[local]babel generateScopedName与 webpack css-loader 配置的localIdentName要保持一致, 本来要配置不同的hash值确保真正的名字不重复[path][name]__[local]__[hash:8]结果打包出来的代码 css样式 class 与 html属性的class 名字hash不一致, 不知道怎么解决,未完待续~~ TODO。
配置完成的代码如下
import React from 'react'
import './index.module.less'
// 使用了 styleName 的类名就是模块化的class 通时也可以与className混合使用
const CssModules = (props) => {
  return (
    <div styleName="css-module">CssModules</div>
  )
}
export default CssModules 
 京公网安备 11010502036488号
京公网安备 11010502036488号