打包分析,是指当我们使用webpack 对项目进行打包之后,可以使用打包分析工具,来对我们打包生成的文件进行分析,来看看打包是否合理。
webpack 官方提供了它的一个分析工具的github 仓库:https://github.com/webpack/analyse
下面我们就来使用一下这个分析工具,来分析一下我们打包生成的代码文件。
我们要对我们打包的代码进行分析,首先要生成一个打包过程的描述文件。这个在官网的README 中有写,在运行webpack 的时候,加上“--profile --json > stats.json”
那么我们打开项目的package.json, 我们在script 中添加上上面的配置,如下。
"scripts": {
"bundle": "webpack --profile --json > stats.json",
"start": "webpack-dev-server"
},
然后,我们重新运行打包命令.
完了后,会发现在项目根目录下,多了一个文件 stats.json 。这个文件里面的内容,就是对打包过程的描述。
当然,我们也可以自己看这个文件,这样会比较不友好,很多项。我们可以借助一些工具,来分析这个文件的内容。
在webpack/analyse 的README 中,给出来这个工具的地址:http://webpack.github.io/analyse/
我们进入这个网址。然后把刚刚生成的stats.json 传上去。就会显示,如下结果。
这是Home 页,点击Modules 可以看到各模块之间的关系。
这是官方提供的工具。除了官方工具,还有很多工具,可以使用。
我们打开webpack 官方文档 documentation > guides > code splitting > bundle analysis
它也列出了很多工具, 比如 webpack-chart , webpack-bundle-analyzer 可以试试。
接下来,Preloading, Prefetching 。
我们在webpack 配置项中,optimizaiton.splitChunks.chunks 不是‘all’ 而是‘async’。也就是默认只对异步加载代码进行分割。而代码分割,比如 lodash 实际上,是对第二次加载时会有性能提升(从缓存中获取),而对第一次页面加载是没有大的提升的(除了页面加载后触发某一条件下才进行异步加载)。
而真正对页面性能做优化,我们希望的是,当第一次访问页面的时候,它的加载速度就是最快的。那么上面的仅进行代码分割,是满足不了需求的。所以就有了Preloading,Prefetching。
我们先将项目webpack.config.js 代码分割部分配置改简单一些:
optimization: {
splitChunks: {
chunks: 'async'
}
},
然后,我们把index.js 中的代码稍微改一下。
document.addEventListener('click', () => {
const element = document.createElement('div');
element.innerHTML = 'hello hello';
document.body.appendChild(element);
})
然后,打包,运行页面。
那么这段代码是否有优化空间呢?有!
我们在页面里打开(chrome)控制台,然后在控制台里输入 Comand+Shift+p , 然后在搜索框中输入 coverage 关键词,打开show coverage .如图
然后我们点击左边录制的圆点,刷新。下面显示了,代码未使用率是17%
实际上,我们看到,document 的click 的事件处理函数代码,它在页面刷新的时候是放在Unused Bytes 中的。如果这部分代码量很大,而它在页面加载的时候不需要用到,那么把它放在页面加载的时候一起加载,是会拖慢页面首次加载的性能的。
因此,比较好的方式是,这种异步交互的代码,放在一个单独的模块中编写。
比如,我们在src 下新建一个click.js 文件,如下。
function handleClick () {
const element = document.createElement('div');
element.innerHTML = 'hello hello';
document.body.appendChild(element);
}
export default handleClick;
然后,再修改一下index.js 文件,如下。
document.addEventListener('click', () => {
import('./click.js').then(({default: func}) => {
func()
})
})
下面,我们运行打包命令,然后重新刷新一下页面,发现,Unused Bytes 百分比反而增加了。这是为什么呢!因为我们的代码量变少了!hhh...
来看看,打包后的代码,如下。click 事件处理函数现在变得比以前少了,因此分母小了。
当然,在我们点击页面后,百分比就降下来了。
这种方式,是让页面较快加载的方式!
当我们用这种方式编写代码的话,我们是可以在交互的时候,再去发送网络请求,加载交互代码,但是这也有一个问题。当交互发生时,才去网络请求代码,然后执行代码,那么可能会影响交互性能。那么我们希望首屏核心代码加载不会变慢,后续交互操作也很顺畅,那么就可以通过,在首屏核心代码加载后,再程序自己去加载一些交互代码,类似偷偷地加载。这种方式,就得依赖Preloading, Prefetching 这种打包特性来实现。
参考官网:documentation > guides > code splitting > Prefetching/Preloading Modules
使用magic comment 语法,如下(index.js)
document.addEventListener('click', () => {
import(/* webpackPrefetch: true */ './click.js').then(({default: func}) => {
func()
})
})
上面的意思是,当点击页面的时候加载click.js,但不是非要等到点击的时刻去加载。一旦发现,目前主要的js 都加载完成了之后,网络带宽有空闲的时候,就可以加载了。
然后打包,刷新页面。会发现,分割的js 文件,就不是在点击页面的时候加载了,它会提前加载。再点击页面的时候,就会使用缓存中的文件了。
preloading 与 prefetching 的主要区别是,prefetching 的代码会等到主要的js代码加载完 浏览器空闲时才会去加载,preloading 代码会与主要的js 代码一起加载。官网解释如下,更详细的应该翻阅官网。
因此,这里推荐的提高性能的一个思路是,提高代码的利用率,把一些代码放入异步加载中,使用懒加载 prefetching 的方式加载。