/antd-custom

antd-react-webpack4-redux-saga-router4 该基础框架采用第三方依赖包都是目前最新版本,结合antd的UI框架,实现主题定制,webpack自主配置,动态菜单路由设计,redux数据管理,国际化多语言,错误统一处理,本地mock服务等功能

Primary LanguageJavaScript

antd + react + redux + webpack4 + react-router4基础框架


预览地址:https://awesome-goldstine-efd4b4.netlify.com/#/app/exchange

说明

该基础框架采用第三方依赖包都是目前最新版本,结合antd的UI框架,实现主题定制,webpack自主配置,动态菜单路由设计,redux数据管理,国际化多语言,错误统一处理,本地mock服务等功能。

设计原则:

1)开放性:秉承自由开放精神,绝对不会给大家造任何的黑盒子(啥叫黑盒子,就是它规定你怎么写,你就得怎么写,它不更新,你也休想正常使用,当然除了react,antd等第三方框架),项目中数据管理(伪dva),webpack配置,动态路由设计等,都是采用自由开放**,大家可根据自己实际项目需要自行修改。

2)多选择性:该框架有目前存在多个版本供大家选择

1)test分支(min版本,里面只有静态菜单路由,数据管理,按需加载几个简单功能,适合小型项目)

2)master分支(默认,稳定版本)

3)develop分支(开发版本,最新的版本)

4)pre分支(健全版本,由min版本升级后的第一个版本,功能相对健全)

功能说明:

初始化项目

$ npm i generator-antd-custom -g
$ cfe init react

切换至指定版本分支(需要用git初始化项目)

$ git init
$ git clone https://github.com/ctq123/antd-custom.git
$ git checkout N
N = test || develop || pre

运行

$ npm start

or

$ npm run dev

打包

$ npm run build:test

or

$ npm run build:prod

结构说明

目录结构

语法规范说明

1.针对tabs业务模块,不再使用文件名称代表某个业务模块,建议使用目录名称替代。比如主页模块,目录名称home就代表home业务模块,home目录下采用index.js作为模块入口

2.由于react已经采用react16.9版本,切勿继续使用componentWillMount和componentWillReceiveProps等即将要废弃的生命周期,免得要在react17版本中增加维护成本

...

功能说明

1.菜单配置

新增一个菜单需要两个步骤:

1)在menus中的menu.data.js文件中添加新菜单,其中key和path都是唯一值(其中permKey是权限字段,权限控制中会介绍)

2)在对应的tabs模块下添加index.menux.js文件配置,其中key和path要与步骤1中保持一致(其中routeProps是路由属性,动态路由中会介绍)

涉及范围:

menus/menu.data.js

tabs/../index.route.js

2.动态路由

项目中采用react-router4管理路由,路由又分为静态路由和动态路由

1)静态路由:src/App.js中转跳app页面和login页面采用的是静态路由设计,它属于页面层级,并不会因为用户权限的差异而不同

2)动态路由:pages/app/index.js中采用的是动态生成路由,它会因为用户权限的不同而存在差异

动态生成路由逻辑:

1)先去pages/tabs业务目录下查找出所有业务模块index.route.js文件

2)根据index.route.js查找routeProps属性生成Route

3)根据传入用户菜单列表的menuList查找父级菜单下的第一个子菜单生成Redirect(业务需求:点击父级菜单路由会转跳至第一个有效子菜单)

详情:menus/menu.route.js

涉及范围:

pages/app/index.js

menus/menu.route.js

tabs/../index.route.js

app内菜单处理技术方案:

1)路由转跳方案:点击菜单加载tab页面文件,转跳到对应的路由并渲染页面,本项目采用的是路由转跳处理方案

2)新增tab方案:点击菜单加载tab页面文件,新增tab渲染页面,若采用该技术实现方案,可将上述涉及范围删除

3.权限控制

用户登陆成功后,会向后端获取用户权限列表,是一个权限key值的列表,分为菜单权限和功能权限

1)菜单权限: 在menus/menu.data.js路由中配置permKey值,获取到用户权限列表后,重新生成新的菜单列表

2)路由权限: 在index.route.js路由中配置permKey值,获取到用户权限列表后,重新生成新的路由列表

3)功能权限: 若页面中存在某个按钮只有某些角色(权限)才能看到,根据用户权限列表判断是否需要显示该按钮,如tabs/example模块

涉及范围:

pages/app/index.js

menus/menu.data.js

menus/menu.permission.js

utils/permission.js

tabs/../index.route.js

更多权限技术方案:

1)前后端结合方案:用户登陆后,获取用户权限列表,前端根据用户权限列表生成相应的菜单,但需要前端配置菜单权限key值;

优势:路由权限和功能权限可以一次性获取

2)后端判断方案:用户登陆后,后端判断该用户权限,直接返回菜单列表,前端直接显示

优势:前端不需要重新生成菜单,也不需要配置权限key值 劣势:若有菜单内颗粒度更小的按钮功能等权限时,还需要再次向后端获取权限列表值,判断是否需要显示

4.数据管理

数据管理采用的是redux+redux-saga方案,这里参考dva的部分设计**,自己重新生成一套数据管理方案,使用方法与dva非常相似

如果你还不熟悉reduxredux-saga,请自行学习和了解

如果你不想了解它们,那也没关系,按照以下两个步骤使用即可,不过我还是建议你抽空学习并掌握它们

新增一个菜单数据管理需要两个步骤:

以tabs/home模块为例

1)在home模块下创建一个index.model.js文件,其包含四个属性name, state, reducers, effects。

name: model名称,model中的name需要保持唯一,它是挂在store下state对应的key值

state: 初始state状态

reducers: reducer纯函数对象

effects: redux-saga处理异步请求的副作用对象

2)在home/index.js文件中使用connect()引入该模块的state属性,即步骤1中的name,它对全局有效

说明:reducers和effects函数的key,最好以步骤1中的name开头,调用时业务清晰明了,同时方便后期全局的搜索和维护

涉及范围:

pages/../index.model.js

pages/../index.js

redux/*

核心设计**:

1)遍历pages/*目录下index.model.js找出{name, state, reducers},并将其重新整合生成redux的reducer

2)遍历pages/*目录下index.model.js找出effects,将其重新整合生成redux-saga

更多详情请看redux最佳实践的前世今生2

redux建议使用场景

1.兄弟组件之间通信,不建议在直接父子组件中滥用redux

2.跨多层父子组件之间通信,比如爷爷和孙子,曾祖父和曾孙等。redux原理之一就是基于这个来做的。

理论上,在index.js中直接使用axios处理异步已满足百分之九十业务场景了,比如user模块;

只有一些跨兄弟组件通信或者跨多层父子组件才需要使用redux,比如login模块;

home模块中的示例只是为了方便介绍redux的使用方法,故意而为之

5.按需加载

按需加载处理方案:

1)采用require.ensure()处理

2)采用react.lazy()处理,react16.6引入,利用import()原理处理懒加载,暂时还不支持服务器渲染

3)采用loadable-components处理,支持服务器渲染,也是react官方推荐处理服务器渲染方案。

后面两种方案都是采用import()原理进行代码切割,并使用promise进行异步加载。

由于本项目不涉及服务器渲染,采用第二种技术方案,更重要的是它是react原生官方的,是亲生儿子,比任何第三方插件都可靠。

6.国际化语言

目前处理国际化语言最流行的两种解决方案是react-intli18n

本项目采用的是antd官方使用的一套国际化插件,react-intl3,也是非常流行的一套解决方案,但目前网上的文章多是react-intl2的,react-intl3配置和使用方法均出现了较大的变化,具体可看官网。

react-intl有两种使用方法:

1)dom场景:直接生成HTML对应的翻译文本dom,如home模块(home/index.js)

2)字符串场景:直接生成string字符串翻译文本字符串,如user模块(user/index.js)

实际使用场景中需要在locales/目录下配置对应的key-value值,通过引用key来显示对应的文字

涉及范围:

src/components/react-intl/*

locales/*

pages/../index.js

src/components/*

7.本地mock服务

这里采用的是一个外部插件cf-mock-server作为本地mock服务,具体配置可在webpack中配置

webpack.config.js

module.exports = {
  //...
  devServer{
  //...
    after: (app, server) => {
      app.use(mock({
        config: path.join(__dirname, './mock-server/config.js')
      }))
    },
  }
}

然后创建mock-server文件夹及其配置文件config.js,最后在config.js配置对应的接口API以及对应json文件数据即可,具体可看home模块的例子

涉及范围:

mock-server/*

8.路径alias别名

通过创建import或require的别名,来确保模块的引入变得更简单。

webpack.config.js

module.exports = {
  //...
  resolve: {
    alias: {
      '@assets': path.join(__dirname, 'assets'),
      '@src': path.join(__dirname, 'src'),
      '@components': path.join(__dirname, 'src/components'),
      '@utils': path.join(__dirname, 'src/utils'),
      '@menus': path.join(__dirname, 'src/menus'),
      '@locales': path.join(__dirname, 'src/locales'),
    }
  }
}

通常我们使用的编辑器是vscode,上述只对webpack有效,vscode编辑器它并不知道,command+点击并不会发生转跳,因此需要添加jsconfig.json配置

jsconfig.json

{
  "compilerOptions": {
    "checkJs": false,
    "allowSyntheticDefaultImports": true,
    "baseUrl": ".",
    "paths": {
      "@assets/*": ["assets/*"],
      "@src/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"],
      "@menus/*": ["src/menus/*"],
      "@locales/*": ["src/locales/*"],
    }
  },
}

9.错误统一处理

我们采用axios作为通信处理方式,在应用页面初始化时,设置axios,并对url返回进行拦截处理,若出现错误异常,会出现两种场景,一种是网络错误,一种是业务错误

1)网络错误:网络请求状态为401,404,503等错误,并提示对应的信息。

2)业务错误:这种没有网络异常(即返回状态为200),通常后端返回的数据都会经过一层包装,若数据中存在success的状态,若为false,即发生了业务异常,需要对其进行特殊处理。本项目中业务异常返回到各模块中进行处理,在拦截层做统一处理,若成功直接返回真正可用的data数据;若失败,先提取后端返回的错误信息,并与原始数据一起直接返回给view层,至于如何处理错误,由view层自己决定

涉及范围:

utils/handleAxios.js

10.安全CSRF防范

安全方面,我们采用token验证的方式解决

我们通过设置withCredentials携带cookie,前端登陆成功后,我们会从cookie中获取token值,并在axios请求的header中统一设置Authorization字段为token值,以后所有的请求的头部都会带上该Authorization字段,后端根据该字段与后端保存的token进行验证判断该请求是否合法

涉及范围:

utils/handleAxios.js

11.导出当前页

采用xlsx依赖包,它比file-saver更出色,提供多种导出文件格式并且提供样式导出,比如合并单元格等

对antd的columns进行解析和封装,使用时直接调用方法即可,优雅简单,不需要其他配置

涉及范围:

utils/exportTableData.js

utils/download-file.js