升级 React Router 与 Code Splitting 实践
背景
公司某 SPA 项目的代码越来越重,功能模块也越来多;而且里面的一个关键依赖react router
的版本也有’年头’没有更新了。处于对欠技术债的恐惧以及小目标的召唤,就领了这个活:也就是标题说的两件事,升级 react router 到最新版(v4)和使用 code splitting 拆分模块。
Breaking Changes
凡是这种大的重构工程首先要保证的是不影响重构前的所有功能,为了避免产生副作用,好好过一遍官方文档尤其是Change Log
是非常必要的。具体来说就是 react-router-v2 到目标版本 v4 的所有大改动, 点这里查看详情,为了节省时间,我把主要的更新日志列在这了:
[v3.0.0-alpha.2]
Jul 19, 2016
- Breaking: Remove all deprecated functionality as of v2.6.0 ([#3603], [#3646])
- Breaking: Support history v3 instead of history v2 ([#3647])
- Feature: Add
router
to props for route components ([#3486])
–
[v3.0.0-alpha.1]
May 19, 2016
- Breaking: Remove all deprecated functionality as of v2.3.0 ([#3340], [#3435])
- Breaking/Feature: Make
<Link>
andwithRouter
update inside static containers ([#3430], [#3443]) - Feature: Add
params
,location
, androutes
to props injected bywithRouter
and to properties oncontext.router
([#3444], [#3446])
官网非常贴心,给出了详细的迁移指导,为了节省时间,这里总结一下:
- 依赖变化, v2.8.1 => v4.1.2(这不是废话吗)
- 不再支持中心化的全局路由配置
- 抽象出 react router core 包,单独的浏览器应用可以安装 react router dom 这个依赖
- 移除了和 history 有关的接口,可以使用新的组件实现旧有功能
如果要集成 redux 使用,有几点要注意,
安装对应版本的 react-router-redux
1 | npm install --save react-router-redux@next |
使用提供的路由 hoc 关联路由信息到 react component - withRouter
1 | // before |
安装独立的history
依赖, 并且配置 redux store
1 | npm install --save history |
1 | import React from 'react'; |
要更深度整合的话,可以参考这个Deep Intergration?(嘴上说不推荐要深度整合,anyway 还是支持了)
代码分割(code splitting)
分割这事主要分两个方面,一个是怎么才能提取公共的依赖,另一个是使用啥方法可以申明某个组件是要被拆出来做独立 chunk 的;实践下来,发现这两方面的事情都和 webpack 的配置密不可分。使用 webpack2+的版本就可以同时解决这两个问题了。
主流的提取公共依赖的途径有 2 种,Dll 和 CommonsChunk 插件。
Dll+Dll Reference 插件需要配合使用,优点是独立于 webpack 运行时打包,可以减少整体打包所花费的时间。CommonsChunk 配置优化的方法也有许多,在实践过程中,可以灵活配置 minChunks 属性按引用次数和引用位置打包可以得到比较好的效果。
1 | minChunks: number|Infinity|function(module, count) -> boolean, |
还有一种方法就是手动配置 verdor 的 entry,适用于项目不大,开发者对项目依赖关系又非常清楚的场景。总的来说,具体使用哪种方法还是要按照实际项目的情况作选择。
谈到代码分割的另一个方面,如何拆分独立 chunk 也有下面几个可选项。
- dynamic import,使用直观,可以一定程度的做封装
- bundle loader,这个是 react router 官方例子使用的方法,可配置能力强
- proise loader, 和 bundle loader 类似但是是用 Promise 的方式调用
可能遇到的问题
- 很多项目是使用 html plugin 使用模版 html 插入生成的 bundle 文件,使用 dll 插件的时候,html plugin 没法取得对应的 chunk name, 这种情况可以使用add-asset-html-webpack-plugin,估计以后会有新的插件或者接口支持吧
- 查看打包和拆分情况, 推荐使用 webpack bundle analyzer, 用 treemap 的形式直观展示 chunk 的分布和占比情况,还可以设置 gzip 或者原始输入大小 (parse gzip stat)