一、前言
通过上篇文章的学习,我们可以在项目中使用 ES6 的语法来编写代码了,下面让我们继续 Webpack 的学习之旅。
项目结构
本文会以 demo04 作为项目文件夹,各文件直接拿之前项目 demo03 的,接下来做些修改。
1 | { |
1 | { |
1 | ... |
1 | { |
src 目录下新增 math.js
1 | const add = (a, b) => a + b |
1 | import { add } from './math.js' |
安装依赖
1 | npm ci |
运行打包命令
1 | npm run bundle |
打包成功,dist 目录下 index.html 放入浏览器运行,观察控制台打印结果。
二、Tree Shaking
本节内容主要以了解为主。
在 math.js 中我们定义了方法 add() 以及 minus(),接着在 index.js 中引入 math.js 这个模块,并使用了里面的 add()。查看打包生成 dist 目录下 main.js 的代码,在里面搜索 add 以及 minus,我们会发现虽然没有使用 minus 这个方法,但它一样被打包到了 main.js 里面去,这样是没有必要的,只会增加打包后 main.js 文件的体积。
这时候我们就需要使用到 Tree Shaking,翻成中文的意思就是“摇树”,把一个模块内不需要的内容都摇掉。
Tree Shaking 只支持 ES Module 的引入,因为 ES Module 底层是静态引入的方式,而 CommonJs 是动态引入的方式。
我们可以把 math.js 理解为一棵树,它里面会导出很多内容,这些内容可以理解为小的树形结构,这里我们只会引入树的一部分,引入的内容进行打包,不引入的内容 Tree Shaking 帮助我们剔除掉、摇晃掉。
Development 模式默认是没有 Tree Shaking 功能的,需要我们做些配置。
1 | ... |
当项目中进行了例如 import ‘@babel/polyfill’ 的操作,@babel/polyfill 这个包实际上不会导出任何的内容,它是在 window 对象上绑定了一些全局变量,并没有直接去导出模块。
使用了 Tree Shaking,它发现这里没有导出任何内容,于是打包的时候可能就忽略了 @babel/polyfill 这个包。这时候需要我们在 package.json 内 sideEffects 设置 @babel/polyfill,Tree Shaking 就不会对它进行处理。
同理,当项目中进行了类似 import ‘./style.css’ 的操作,可以在 sideEffects 里进行设置避免 css 文件被 Tree Shaking 剔除。
我们代码中没有引入 @babel/polyfill 这样的包,也没有引入样式文件,不需要 Tree Shaking 去避免什么文件,就可以把 sideEffects 设为 false。
package.json 内不能写注释,本文只是方便说明如何使用。
1 | { |
这时候运行打包命令,然后观察 dist 目录下 main.js,依然能在该文件中找到 minus 被打包了,但我们可以看到多了一行注释说导出了 add minus,使用了 add。
Development 模式在做打包的时候,即使你使用了 Tree Shaking,它也不会去剔除没使用的内容,只会在代码里提示你一下。因为在开发环境下的代码,一般都需要做些调试,如果 Tree Shaking 把一些代码剔除掉了,那可能调试的时候一些代码的行数 SourceMap 在对应的时候会出错。
Production 模式下自动开启 Tree Shaking,不需要我们去设置 webpack.config.js 内 usedExports,package.json 的 sideEffects 也会有默认的配置。
开启 Production 模式,并进行相关设置。
1 | ... |
1 | { |
因为 Production 模式会对打包的代码进行一个压缩处理,待会在 main.js 里面是搜索不到 add、minus 这些方法名的,那就不好查看 Tree Shaking 是否生效,我们需要对源代码进行一些修改。
1 | const add = (a, b) => { |
1 | import { add } from './math.js' |
运行打包命令,打包成功,观察 dist 目录下 main.js,对 console.log 进行搜索,只能搜索到一个 add 的实现,证明 Tree Shaking 生效了,没有对 minus 进行一个打包。
Tree Shaking 的作用就是减少生产环境的打包体积,在引入一个模块的时候,不引入所有的代码,只引入需要的代码进行打包。它在开发环境中没有什么优势,也不会生效。生产环境中会自动开启 Tree Shaking,并且有相关的默认配置,一般不需要做修改,除非自己引入的某个模块因为 Tree Shaking 而被剔除了导致项目不能正常运行,这时候就需要设置下 package.json 的 sideEffects,告诉 Webpack 这个模块不需要 Tree Shaking 去处理。
三、Development 和 Production 模式的区分打包
代码压缩
开发环境的代码一般不需要压缩,生产环境的代码默认压缩。
SourceMap
开发环境设置的 SourceMap 一般提示比较全,帮助我们快速定位问题。
生产环境是准备上线的代码,这时候 SourceMap 也就不是很重要了,可以不设置,或者生成个 .map 文件来存储。
webpack.config.js 配置文件
因为开发环境和生产环境的打包配置内容有些区别,在切换模式打包的时候,往往需要去修改 webpack.config.js 里的配置项,比较麻烦。
我们可以把 webpack.config.js 拆分为 webpack.common.js、webpack.dev.js、webpack.prod.js,用 webpack.common.js 存储开发环境与生产环境共有的配置项,用 webpack.dev.js 存储开发环境需要的配置项,用 webpack.prod.js 存储生产环境需要的配置项。
webpack.dev.js 与 webpack.prod.js 都需要合并 webpack.common.js 才能得到各自环境需要的完整配置项,于是需要用到一个第三方模块 webpack-merge。
安装 webpack-merge
1 | npm install webpack-merge@4.1.5 -D |
删除项目根路径下 webpack.config.js,新建与 src 目录同级的 build 目录来管理配置文件,build 目录内新建 webpack.common.js、webpack.dev.js、webpack.prod.js。
1 | const path = require('path') |
1 | const webpack = require('webpack') |
1 | const merge = require('webpack-merge') |
因为开发环境与生产环境使用的 Webpack 配置文件不一样,所以 package.json 里面的 scripts 脚本命令也要做相应的更改。
1 | { |
这时候运行开发环境启动服务的命令,以及运行生产环境项目打包的命令都不会有问题。
1 | npm run dev |
1 | npm run build |
四、Webpack 和 Code Splitting
自己处理的 Code Splitting
Code Splitting 就是代码分割,我们看个例子。
首先安装下 lodash,它是一个功能集合,能够提供很多的方法,比如可以高性能地去使用一些字符串拼接的函数等等。
1 | npm install lodash@4.17.11 --save |
然后在 index.js 里去引入这个库并使用。
1 | import _ from 'lodash' |
运行打包命令
1 | npm run build |
打包成功,把 dist 目录下 index.html 放入浏览器运行,控制台正常打印结果。
接着来查看下 dist 目录下 main.js 内代码。
我们可以看到 lodash 这个工具库以及 index.js 编写的业务代码都被打包到了 main.js 里面去。
这会带来一个潜在的问题,假设 lodash 的大小为 1mb,业务代码的大小也为 1mb,打包生成的 main.js 在 2mb 左右,那当用户去访问 index.html 时就需要等待一个 2mb 的 js 文件加载完,才去执行里面的业务逻辑展示相应页面。带来的问题就是打包文件很大,页面加载时间长。
当修改了业务逻辑并重新打包,这时候用户访问页面又要去重新加载这个 2mb 的文件内容,这样的体验显然是很差的。
我们来做下代码分割,在 src 目录下新建 lodash.js,把 index.js 中对 lodash 的引入放到 lodash.js 中进行。
1 | import _ from 'lodash' |
1 | // import _ from 'lodash' |
设置新的入口文件
1 | ... |
运行打包命令,打包成功,index.html 放入浏览器正常运行,控制台打印出结果。
生成的 dist 目录内容如图所示
现在原先假设 2mb 的 main.js 被拆分为 lodash.js(假设 1mb)和 main.js(假设 1mb)。
浏览器是支持并行加载文件的,同时加载 2 个 1mb 的文件可能比原先加载一个 2mb 的文件快一些,重点是当业务代码发生变更重新打包,用户只需要重新加载 main.js,原先的 lodash.js 没有任何变更,在浏览器缓存里取即可,这样就提高了网页的加载速度。
这种代码的拆分就是 Code Splitting,Code Splitting 本质上和 Webpack 是没有关系的,是一个单独的概念,用来提高整个项目的性能,但 Webpack 有些插件能够帮助我们方便地进行 Code Splitting,在 Webpack4 中就有个内置插件做代码分割,直接使用即可。
Webpack 的 SplitChunksPlugin
上面是我们自己处理的代码分割,接下来我们通过 Webpack 自带的 SplitChunksPlugin 智能地去实现 Code Splitting。
同步
配置文件去除 entry 里的 lodash,新增 SplitChunksPlugin 相关配置项。
1 | ... |
修改 index.js,再次引入 lodash。
1 | import _ from 'lodash' |
运行打包命令,打包成功,这时候 dist 目录下会新增文件 vendors ~ main.js 以及 vendors ~ main.js.map。
vendors ~ main.js 里面存放的就是 lodash 工具库的相关代码。vendors ~ main.js.map 是前文提到过的 SourceMap,对应源代码的映射关系。
这时候 main.js 就只是存放 index.js 内的业务代码,达到了我们想要的代码分割效果。
异步
上面是同步引入模块的处理方式,当我们异步引入模块时的处理方式有些不一样。
修改 index.js 来异步引入模块
1 | function getComponent () { |
optimization 里可以不用配置
1 | ... |
运行打包命令,打包成功,这时候 dist 目录下同样会生成文件来存取 lodash 工具库的相关代码,本文中生成了 1.js 以及 1.js.map。
当我们不想 lodash 生成的打包文件名为 1.js,而是打包后叫做 lodash.js,这时候就用到 Webpack 的 Magic Comments,也就是“魔法注释”。它可以添加到异步加载模块的代码中,变更打包后的文件名。
1 | function getComponent () { |
运行打包命令,观察生成的打包文件,原先的 1.js 变成了 vendors~lodash.js,而不是我们想要的 lodash.js,这是因为受到了一些默认配置项的影响。查看官网 SplitChunksPlugin 的相关介绍,我们对 Webpack 打包的配置文件进行一些设置。
1 | ... |
运行打包命令,打包成功,lodash 工具库生成的打包文件名为 lodash.js。
小结
同步或者异步引入的模块,Webpack 都能够进行代码分割,它的底层是使用了 SplitChunksPlugin 这个插件。
同步的代码分割实现方式是以同步的形式引入模块,接着配置 Webpack 打包配置文件内 optimization 下的 splitChunks 开启代码分割。
异步的代码分割实现方式是以异步的形式引入模块,就直接能进行代码分割。当对打包生成文件有相应要求就需配置 Webpack 打包配置文件内 optimization 下的 splitChunks 配置项。
五、SplitChunksPlugin 配置参数
SplitChunksPlugin 这个插件的配置项比较多,接下来我们通过查看配置参数及相应的注释来理解这些配置项的作用。
1 | splitChunks: { |
除了默认配置,当我们想对打包后生成的文件名进行自定义,还可以进行一些设置。
1 | splitChunks: { |
虽然 cacheGroups 内的 filename 不能用于引入的异步模块进行代码分割时自定义文件名,但是引入的异步模块可以使用上面提及的“魔法注释”。
splitChunks 内配置项比较多,实践出真知,建议小伙伴们尝试编写一些 demo,看修改一些配置项后的打包效果有何区别。
六、官方文档
学习完以上内容,可以前往 Webpack 4.0 官网 阅读相关文档。
建议阅读的内容为:
(1)文档 > PLUGIN,页面左侧的 SplitChunksPlugin。
七、总结
通过以上的学习,我们了解了什么是 Tree Shaking、知道了 Webpack 开发环境和生产环境打包的区别,对打包的配置文件进行了拆分、知道怎样进行代码分割以及 Webpack 自带的代码分割插件 SplitChunksPlugin 配置参数的作用。
本文最后的代码我会上传到 码云(gitee.com/phao97)上,项目文件夹为 demo04。
如果觉得本篇文章对你有帮助,不妨点个赞或者给相关的 Git 仓库一个 Star,你的鼓励是我持续更新的动力!
- 本文标题:Webpack 4.0 学习笔记(四)
- 本文作者:phao
- 创建时间:2022-05-08 16:57:07
- 本文链接:http://phaode.cn/2022/05/08/Webpack-4-0-学习笔记(四)/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!