一站式搞明白webpack中的代码分割

论坛 期权论坛 期权     
linux全栈编程   2019-7-14 23:35   2969   0
上次分析到通过devtool的配置项来设置source map,在线上环境可以通过设置成cheap-module-source-map来生成单独的map文件,但是map文件在线上环境会不会每次都加载呢?如果加载的话,会不会造成带宽和请求的浪费呢?
笔者也有这样的疑问,于是在网络上查了一下。后来才了解到,在浏览器中,只有在调试模式下才会自动下载map文件,平时线上环境的话,是不用担心这个问题的。

在使用webpack构建单页面应用时,为什么要进行代码分割?
  • 如果entry中只配置了一个入口,那么如果从这个入口文件开始引入的公共库的代码过大,打包出来会只有一个bundle文件。当浏览器端加载这样一个体积太大的bundle时就会造成加载过慢。
  • 通常来讲我们的公共库代码是不经常修改的。但是我们的业务代码是经常要修改的,如果打包到不同的bundle文件可以更好的利用浏览器的缓存,在浏览器端来复用之前的公共库的bundle文件。
那如何通过代码分割来优化呢?

在webpack3中可以通过CommonsChunkPlugin来进行代码分割,但是webpack4中,官方提倡使用SplitChunksPlugin这个插件,所以本次主要讲解的也是在webpack4中通过SplitChunksPlugin这个插件来实现代码分割的方法。

首先要先弄懂一个预备知识点,就是在webpack中,什么叫做chunk,什么叫做bundle?
下面这种图可以帮助我们更好地理解:


在webpack的打包配置entry中有两个入口:index和utils。分别对应index.js文件和utils.js文件,其中index.js文件引用了common.js和index.css。那么webpack打包的时候就会把前三个文件作为一个chunk,utils.js文件作为一个chunk,但是在webpack配置中国年用MiniCssExtractPlugin插件抽离出了css文件,所以产生了.css和.js两个bundle文件。总之,chunk是webpack打包时候的一个概念,而产生出来的单个文件,可以在浏览器中运行的就叫做bundle。

其实上文中通过在webpack配置文件中配置两个entry入口,其中utils把一些工具函数挂载到全局,也可以算是一种代码切割,但是有没有更加灵活的方式呢?我们来看下SplitChunksPlugin插件。

SplitChunksPlugin插件的介绍可以在webpack的官方文档中找到:


从文档中可以看到SplitChunksPlugin的默认配置如下:


下面会一点点来讲解。

代码分割有两种场景:一种是同步引入代码模块的分割,还有一种是异步引入的代码模块的分割。

一、同步代码分割:
在业务代码中引入lodash,通过之前讲的webpack-bundle-analyzer插件分析打包情况如下:


可以看出我们的src文件和node_modules文件都是在一个bundle中。如果我们想把node_module中的库文件单独打包到一个chunk中,我们可以把官网上的SplitChunksPlugin的默认配置拷贝到打包配置文件中,然后做如下修改:

其中chunks表示可以配置成initial、async、all分别表示静态引入、动态引入和两种情况下来进行代码分割。
minSize为可以进行代码分割的最小包体积,默认为30000Byte。而在打包分析中我们看到lodash的包体积在webpack处理完后是将近70KB,大于SplitChunksPlugin的最小打包体积。所以在做如上修改以后,便能够将lodash打包到单独的chunk中。打包后的情况如下:


从图中我们可以看到,lodash被单独打包到了叫做vendors~index.js的chunk中。
下面再来完善一下上面分析的打包流程,满足了minSize的条件以后,顺着条件往下走可以走到cacheGroups配置,cacheGroups中有不同的对象,对象中的test即为测试正则,当引用的模块名满足了这个测试正则后,才会按照当前对象的规则打包。
vendors这个键名即是打完包的前缀名,而automaticNameDelimiter中配置的就是前缀后面的连接符,后面就是引入此模块的entry中配置的入口文件名index。

如果我们想要,自定义lodash打包的文件名,我们可以在vendors中配置filename为vendors.js如下:


那么打完包的lodash的chunk就叫做vendors.js了,而cacheGroups上方的name:true的意思就是使cacheGroups中的filename生效。

接下来介绍在splitChunks中剩余的配置:
minChunks是什么意思呢?他的意思就是在entry中配置的入口,引用了该模块的最少个数,只有满足了最少有minChunks个chunk引用了当前模块,才会进行单独打包。
maxAsyncRequests表示代码分割完后,同时可以请求的最大数量。
maxInitialRequests表示首页中的最大请求数,如果代码分割后,首页请求的请求数大于这个数,就不会再继续代码分割了。

二、异步代码分割:
大家可能还不是很熟悉异步加载的语法,可以在官网的API部分中找到import异步加载的语法说明,其实就是ES6的一种语法:


而在webpack的使用中,webpack通过一种叫做“魔法注释”的功能丰富了打包功能,下面会详细看到。
在webpack中SplitChunksPlugin默认就会对异步引入的代码进行分割,但是为了支持异步引入模块的语法,我们还是需要安装babel的plugin-syntax-dynamic-import插件来支持异步加载的语法。
下面来详细看下我们的业务代码:
在index.js中,我们通过click事件动态引入了handleClick.js文件,其中注释的部分就是通过魔法注释来给分割的chunk命名为handleClick。


在handleClick.js文件中,我们为文档添加元素(其中process.env.NODE_ENV是运行时环境变量,可以参考之前几期的推送)。



从下面的运行效果我们可以看到,当网页刷新的时候,先加载出来index.html文件和index.js文件。当点击网页的时候,才会去请求handleClick.js文件,并且网页上出现“123”。


其实异步代码分割还有一个大家可能更熟悉的名字——懒加载。

但是,如果被异步加载的模块很大,我们在真实项目中操作完了再临时请求,会不会还是加载很慢。为了解决这个问题,可以使用魔法注释中的webpackPrefetch来让浏览器加载完首页的js文件后空闲的时候去加载异步分割的bundle文件。




参考:
https://segmentfault.com/q/1010000000396077
https://juejin.im/post/5cede821f265da1bbd4b5630#heading-15
https://coding.imooc.com/lesson/316.html#mid=22363

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP