- 背景
- 现有路由
- 动态路由
- Webpack 配置
- 路由配置
- 优化结果
之前分别从原生层面以及 H5 层面对 HTML 的加载过程进行了分析,确定出加载的耗时主要出现在渲染阶段,但是页面本身并不复杂,dom 的结构也很简单,应该渲染很快才对,所以又通过 chrome 的 timeline 来进行更详细的分析开发文档,结果如下图,可以确定出在一次加载过程中主要的耗时在 JS 的运算上面。
由此可以引发出一些思考,因为用来做分析的页面本身很简单,没有很多的 js 加载才对。经过分析应该是随着 react 工程增大,路由逐渐变多,经过打包以后多个路由的js打包在了一块所以即使只加载一个路由,也会加载很多其他的东西。
现在林林总总路由加起来有二十多个。经过打包以后的js大小有2M多。这就势必拖慢了加载的速度,因为Android进行js运算是很耗时的,在不考虑优化 js 逻辑相关的代码前,使用动态路由技术来对代码进行分离,做到按需加载应该能够提高加载速度。
"/" component={WaterIndex} />
<Route path="/AddWaterMeter" component={AddWaterMeter} />
<Route path="/ConfirmMeter" component={ConfirmMeter} />
...
<Route path="/FeedbackList" component={FeedbackList} />
<Route path="/PaymentHelp" component={PaymentHelp} />
Router>
原理就是将当前的代码在打包过程中分拆成多个小的包,在用户浏览过程中进行按需加载。示例代码
首先在 webpack.config.js 的 output 内加上 chunkFilename
output: {
path: path.join(__dirname, '/../dist/assets'),
filename: 'app.js',
publicPath: defaultSettings.publicPath,
// 添加 chunkFilename
chunkFilename: '[name].[chunkhash:5].chunk.js',
},
name 是在代码里为创建的 chunk 指定的名字,如果代码中没指定则 webpack 默认分配 id 作为 name。chunkhash 是文件的 hash 码,这里只使用前五位。
之前的路由就像上面配置的一样,现在修改成如下的样子
const AddWaterMeter = (location, callback) => {
require.ensure([], require => {
callback(null, require('./views/AddWaterMeter').default)
}, 'AddWaterMeter')
}
const WaterMeterList = (location, callback) => {
require.ensure([], require => {
callback(null, require('./views/WaterMeterList').default)
}, 'WaterMeterList')
}
const ConfirmMeter = (location, callback) => {
require.ensure([], require => {
callback(null, require('./views/ConfirmMeter').default)
}, 'ConfirmMeter')
}
ReactDOM.render(
<Router
history={hashHistory}
render={applyRouterMiddleware(useScroll())}
>
<Route path="/" Component={WaterIndex}>
<Route path="AddWaterMeter" Component={AddWaterMeter} />
<Route path="ConfirmMeter" getComponent={ConfirmMeter} />
<Route path="WaterMeterList" getComponent={WaterMeterList} />
...
<Route path="FeedbackList" getComponent={FeedbackList} />
<Route path="PaymentHelp" getComponent={PaymentHelp} />
Route>
Router>,
document.getElementById('app')
)
history 不变,将创建的路由传递进去。有几个属性的说明
path
匹配路由,和之前的定义一样
getComponent
对应于以前的 component 属性,但是这个方法是异步的,也就是当路由匹配时,才会调用这个方法。
这里面有个 require.ensure 方法
require.ensure(dependencies, callback, chunkName)
这是 webpack 提供的方法,这也是按需加载的核心方法。第一个参数是依赖,第二个是回调函数,第三个就是上面提到的 chunkName,用来指定这个 chunk file 的 name。
这里有可能会有一个异常:
The root route must render a single element
这是因为 module.exports 和 ES6 里的 export default 有区别。
如果是使用 es6 的写法,也就是你的组件都是通过 export default 导出的,那么在 getComponent 方法里面需要加入 .default。
如果是使用 CommonJS 的写法,也就是通过 module.exports 导出的,那就无须加 .default 了。
经过上面的一通操作,再来看一下页面的加载速度,首先是可以明显的感知到速度变快。通过 timeline 来检测一下
可以看到,速度提升了1s,对产品体验来说是一个很大的提升。
参考文档: