背景
目前的 APP 已经差不多有70%的业务使用 H5 来实现,但是一些页面的白屏现象严重。之前分别从原生层面对 WebView 加载 HTML 以及 H5 层面的 HTML 的加载过程进行了分析,确定出加载的耗时主要出现在渲染阶段,但是页面本身并不复杂,dom 的结构也很简单,应该渲染很快才对,所以又通过 chrome 的 timeline 来进行更详细分析的开发文档,结果如下图,可以确定出在一次加载过程中主要的耗时在 JS 的运算上面。

由此可以引发出一些思考,因为用来做分析的页面本身很简单,没有很多的 js 运算才对。
分析
经过一系列的监控发现加载过程的主要耗时在 js 的运算上面,目标页面并不复杂,仅仅是加载了一个列表而已,没有大量的业务需要进行 js 运算,那这些 js 都是哪来的?又查看了加载以后的 js 竟然有接近 2M,这显然是不正常的,原因出在哪?答案就在 react 项目的打包上,虽然我们的项目已经使用了多页面,但是实际上并不能叫多页面,更准确的应该叫多模块才对,一个模块的所有页面全部在一个总的路由下面,即在一个 html 中,同一模块下的所有页面使用一个 js,而即使通过路由加载一个很简单的页面也有把所有的这些 js 全部加载进来,这些势必造成了长时间的运算。
问题找到了,经过查阅资料发现比较好的解决办法就是使用动态路由。
现有路由
现在林林总总路由加起来有二十多个。经过打包以后的js大小有2M多。这就势必拖慢了加载的速度,在不考虑优化 js 逻辑相关的代码前,使用动态路由技术来对代码进行分离,做到按需加载应该能够提高加载速度。
|
|
动态路由
原理就是将当前的代码在打包过程中分拆成多个小的包,在用户浏览过程中进行按需加载。示例代码
Webpack 配置
首先在 webpack.config.js 的 output 内加上 chunkFilename
|
|
name 是在代码里为创建的 chunk 指定的名字,如果代码中没指定则 webpack 默认分配 id 作为 name。chunkhash 是文件的 hash 码,这里只使用前五位。
路由配置
之前的路由就像上面配置的一样,现在修改成如下的样子
|
|
history 不变,将创建的路由传递进去。有几个属性的说明
path
匹配路由,和之前的定义一样
getComponent
对应于以前的 component 属性,但是这个方法是异步的,也就是当路由匹配时,才会调用这个方法。
这里面有个 require.ensure 方法
1require.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,对产品体验来说是一个很大的提升。
参考资料