一站式搞明白webpack中的打包情况,代码分割

前端 - Webpack常见面试题总结_个人文章 - SegmentFault 思否
webpack中如何分析打包情况?

笔者之前有在引入的axios中添加请求和响应的拦截器,来在请求中添加一些cookie和在响应中做一些服务端错误的给用户的透传。然后在所有用到axios库的业务文件中单独引入。

当时怀疑这种用法会不会让webpack重复引入axios这个库,于是想到了用webpack-bundle-analyzer这个插件来进行分析,webpack-bundle-analyzer插件的介绍可以在webpack的官网中找到。

点击跳转到这个插件的github页面:

可以看到使用非常简单,安装插件,在webpack调试环境的配置文件中的plugins添加插件,就可以在打包的时候,自动打开网页来生成打包分析图如下:

从图中可以看到axios库只被引用了一次,所以并没有造成打包冗余。

还可以通过分析打包插件来分析自己打的包里是否引入了多余的类库,也是很好用的。

在webpack4.0的配置文件中,新增了一个mode配置。可以配置两个参数:development和production。本次来根据webpack作者的一篇文章来介绍mode属性的这两个参数之间的区别。

首先大体介绍一下这两种配置mode配置这两个参数时的不同:

development开发环境注重的是:

浏览器的调试工具。

快速的增量编译。

有用的错误提示。

production线上环境注重的是:

尽量小的目标文件。

运行时代码尽量快速。

忽略只有开发时才用的的代码。

不暴露源码的路径。

方便来使用输出的静态文件。

mode只是借助设置默认的配置属性值来实现的,下面来介绍下mode所影响到的配置。

devtool

在webpack的官网中可以找到devtool的配置说明,下面来重点讲解。

其实devtool就是配置source-map的,而source-map又是做什么的呢?

有时在打包的时候我们可以在目标文件中看到这样map为后缀的文件:

可以把它看成是目标文件和源代码的映射关系,当打包后的代码出现错误是可以把浏览器提示的打包后文件的错误位置映射到源代码相应的位置,这样就可以方便我们定位问题。

而对devtool配置不一样的参数可以影响调试环境和线上的错误提示和目标文件大小,下面我们来具体看下devtool的配置项:

none:不生成source-map的映射关系文件。

cheap:只提示业务代码的出错的行不提示列的位置。

inline:把source-map文件内容融合到打包后的目标文件中。

module:对引入的第三方模块也进行source-map。

eval:用特定函数生成source-map的信息,不生成source-map代码。缺点是错误提示的行不是特别精确,因为是使用转换后的代码生成的映射关系而不是使用源码。

在官方文档中已经通过下面这个图标提示了各种配置的耗时和质量,加号越多速度越快:

当mode为development,那么devtool的默认配置为eval。production时,devtool的默认配置为none。

首先来自定义下production环境下的devtool,在线上同样想要输出源码的错误提示,还要包含第三方模块的source-map,但是只输出行即可。此外需要把source-map文件分离出来让错误提示更加精确,可以设置成:cheap-module-source-map。而开发环境,为了使构建更快,可以加上eval。

参考:

https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a

https://coding.imooc.com/lesson/316.html#mid=22332

 

上次分析到通过devtool的配置项来设置source map,在线上环境可以通过设置成cheap-module-source-map来生成单独的map文件,但是map文件在线上环境会不会每次都加载呢?如果加载的话,会不会造成带宽和请求的浪费呢?

笔者也有这样的疑问,于是在网络上查了一下。后来才了解到,在浏览器中,只有在调试模式下才会自动下载map文件,平时线上环境的话,是不用担心这个问题的。

在使用webpack构建单页面应用时,为什么要进行代码分割?

  1. 如果entry中只配置了一个入口,那么如果从这个入口文件开始引入的公共库的代码过大,打包出来会只有一个bundle文件。当浏览器端加载这样一个体积太大的bundle时就会造成加载过慢。

  2. 通常来讲我们的公共库代码是不经常修改的。但是我们的业务代码是经常要修改的,如果打包到不同的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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值