Jsonz bug-log

后续更新会放在 github.com/jsonz1993/blog

0%

react全家桶 + dva 实践整理总结

项目背景

最近接手组内一个前端大牛的项目,大概理清了里面的总理逻辑与数据流向等, 写个总结 方便以后可以查阅参考。

在之前做的后台管理平台里面,就有用到 dva+antd, 到后面直接用 antd-pro 重新整合了一遍,不得不说 antd-pro 真的是开箱即用 优雅粗暴。

但是里面可能很多和我们平时使用习惯有所不同, 所以这次接触的项目是基于 dva 自己再重组了一下 model router 目录结构等。

使得页面更加的模块化,既 models, routes 不是在一个文件目录里,而是直接按照页面级别来组合。有一个好处就是 本来 models,routes 这些几乎就是不能公用的,那直接按着页面去分 方便问题定位与维护。

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
.
├── README.md
├── config # 一些关于 webpack babel dll 等项目构件的配置
│   ├── babel-plugin-wrap-source.js
│   ├── babel-plugin-wrap.js
│   ├── build.log
│   ├── dist.js
│   ├── dll
│   ├── server.js
│   ├── ssr-middleware.js
│   ├── theme.config.js
│   ├── webpack.config.common.js
│   ├── webpack.config.dll.js
│   └── webpack.config.prod.js
├── dist # 打包好的文件
│   ├── css
│   ├── index.html
│   └── js
├── package-lock.json
├── package.json
├── src
│   ├── assets # 放一些静态资源,如 全局一些 less 文件等
│   ├── common # 公共文件, 如 config request utils 等
│   ├── components # 通用组件, 如 editor loading 等,用了 antd 之后,这个文件夹的文件较少
│   ├── index-ssr.js
│   ├── index.js # 项目入口文件
│   ├── mock # mock文件
│   ├── modules # 业务页面文件夹 代码的绝大部分放在此处
│ └── activity # 业务页面 每个业务页面起一个文件夹
│    ├── index.js # 当前页面的路由及动态加载文件的配置文件
│     ├── model.js # 当前页面的 dva model
│     ├── businessUtil.js #
│    └── view # 当前页面的 view 文件
│    └── index.js
│   ├── router.js # 总路由入口
│   └── services # 后台接口服务
└── webpack.config.js # webpack 配置

项目剖析

index.js

和其他项目一样 index.js 处理的东西很简单

  • 进行一些关于 dva 的配置初始化
  • 加载配置app model require(modules/app/model)
  • 加载配置app router require('./router').default
  • &&&&&&& 启动应用

这里就引出两个分支, model && router 前端架构中比较关心的两个。

router 管理

router.js 里面 定义了主 Component 与默认 url

router.childRoutes 也是使用模块的方式,只引用了几个大模块,如 require('modules/activity')(app),

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Routers = function ({ history, app }) {
const routes = getRoutes(app);

return <Router history={history} routes={routes} render={applyRouterMiddleware(useScroll())} />;
};

function getRoutes(app) {
return [
{
path: '/',
component: App,
childRoutes: [
require('modules/activity')(app),
],
},
];
}

modules/activity/index.js 再去加载构建他的子级路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default app=>({
path: 'url',
getComponent (nextState, cb) {
require.ensure([], (require) => {
app.model(require('./model')); // 加载当前模块的model
cb(null, require('./view')); // 记载当前模块的view
}, '模块名');
},
childRoutes:[
require('modules/child1')(app),
require('modules/child2')(app)
],
...其他配置
})

就这样一层一层的构建自己的路由, 根据页面去配置也可以更加的细腻可控。不会写成长长的一坨。

model

model层用的是 dva 那套, 帮我们整合了 redux, redux-sage and react-router 十分超级好用。

这里只是把目录结构也调整为 页面级别里面,与页面的 路由配置同级。 也是出于更好的定位问题与维护。

一般与model 相关的文件还有各个页面级别文件夹(即modules/activity)里面的 businessUtil.js 文件。

这个文件主要用来辅助处理一些和 model 有关的事情,一般会有三个方法 export { getInitState, convertProps2Params, convertParams2Props }。 分别处理 初始化state, server 端数据与组件 props 之间的差异转换。

mock 数据

mock 数据,用了 dva 自带的那套。

我们平时写接口的时候,会在 services/ 下面起一个文件 里面带有某个接口地址 用来请求数据接口的
然后在 mock/ 下面也会起一个文件 相同的也会带有一个接口地址
这样我们如果这个接口地址改变,岂不是要改两个地方?

为此,我们在 services/ 下面的文件,只是简单的写了一些原始数据,如 url, method 等

services/feature1
1
2
3
4
5
6
//@wrapFunction: common/request
// @1 上面那行注释是干什么用的呢?
export default {
url:'api/feature1',
method:'POST'
}

这样在 mock/ 下面就可以直接引入 services/feature1 就可以拿到相同的url, 接口地址改动的话也只需要改动 services 下的文件。

但是如果只是简单的返回一些原始数据,那和一个 config 文件有何区别,这样完全没有放在 services 下面的必要啊。。。 所以我们用了一行注释来做一些操作

大概的原理是 写了一个 babel 插件,用来处理一些操作。
如果匹配到 //@wrapFunction: 则用后面的链接 require 一个方法来包着页面上的代码。

这样就能起到,前端跑代码的时候 feature1 是一个接口功能的文件, mock 的时候是一个单纯的数据输出文件.

babel 插件自动处理 services/*.js

在上面一小节讲到我们用 babel插件来处理差异化,这里我们具体讲一下是怎么处理的

因为项目用的是 roadhog 搭建的, 所以我们在 .roadhogrc.js 里面配置一下 babel 插件,引进自己插件的文件。 这里我们的插件放置在 /config/babel-plugin-wrap.js;

  1. services/*.js 里面添加一行注释代码用作 babel 识别的标识
  2. 判断页面上有无对应注释
  3. 获取注释与目标路径, 把export default 的输出用函数调用表达式替换掉

具体的 babel插件编写方式可以看官网 https://babeljs.cn/docs/plugins/

了解编译器原理可以参考 上一篇文一步步实现极简编译器 —— 了解编译器原理

结语

剩下的 dll 打包, ssr 以后有用到再去了解。
emmm 最近阿里刚出了一个 umiJs 想起知乎上一个评价 用这一时爽 改起来火葬场。
在用 antd-pro 的时候就深有体会了。 所以建议如果不是比较小的项目或者灵活性要求高的项目, 还是不要上 antd-pro的好。。。