第02讲:如何使用 Webpack 实现模块化打包?
# webpack 配置智能提示
默认 VSCode 并不知道 Webpack 配置对象的类型,我们通过 import 的方式导入 Webpack 模块中的 Configuration 类型,然后根据类型注释的方式将变量标注为这个类型,这样我们在编写这个对象的内部结构时就可以有正确的智能提示了,具体代码如下所示:
需要注意的是:我们添加的 import 语句只是为了导入 Webpack 配置对象的类型,这样做的目的是为了标注 config 对象的类型,从而实现智能提示。在配置完成后一定要记得注释掉这段辅助代码,因为在 Node.js 环境中默认还不支持 import 语句,如果执行这段代码会出现错误
没有智能提示的效果,如下所示
加上类型标注实现智能提示的效果,如下所示:
使用 import 语句导入 Configuration 类型的方式固然好理解,但是在不同的环境中还是会有各种各样的问题,例如我们这里在 Node.js 环境中,就必须要额外注释掉这个导入类型的语句,才能正常工作。
所以我一般的做法是直接在类型注释中使用 import 动态导入类型,具体代码如下:
这种方式同样也可以实现载入类型,而且相比于在代码中通过 import 语句导入类型更为方便,也更为合理。
不过需要注意一点,这种导入类型的方式并不是 ES Modules 中的 Dynamic Imports,而是 TypeScript 中提供特性。虽然我们这里只是一个 JavaScript 文件,但是在 VSCode 中的类型系统都是基于 TypeScript 的,所以可以直接按照这种方式使用,详细信息你可以参考这种 import-types 的文档
其次,这种 @type 类型注释的方式是基于 JSDoc 实现的。JSDoc 中类型注释的用法还有很多,详细可以参考官方文档中对 @type 标签的介绍。
# Webpack 工作模式
Webpack 4 新增了一个工作模式的用法,这种用法大大简化了 Webpack 配置的复杂程度。你可以把它理解为针对不同环境的几组预设配置:
production 模式下,启动内置优化插件,自动优化打包结果,打包速度偏慢;
development 模式下,自动优化打包速度,添加一些调试过程中的辅助插件;
none 模式下,运行最原始的打包,不做任何额外处理。
针对工作模式的选项,如果你没有配置一个明确的值,打包过程中命令行终端会打印一个对应的配置警告。在这种情况下 Webpack 将默认使用 production 模式去工作。
production 模式下 Webpack 内部会自动启动一些优化插件,例如,自动压缩打包后的代码。这对实际生产环境是非常友好的,但是打包的结果就无法阅读了。
修改 Webpack 工作模式的方式有两种:
通过 CLI --mode 参数传入;
通过配置文件设置 mode 属性。
上述三种 Webpack 工作模式的详细差异我们不再赘述了,你可以在官方文档中查看:https://webpack.js.org/configuration/mode/
打包结果运行原理
最后,我们来一起学习 Webpack 打包后生成的 bundle.js 文件,深入了解 Webpack 是如何把这些模块合并到一起,而且还能正常工作的。
为了更好的理解打包后的代码,我们先将 Webpack 工作模式设置为 none,这样 Webpack 就会按照最原始的状态进行打包,所得到的结果更容易理解和阅读。
按照 none 模式打包完成后,我们打开最终生成的 bundle.js 文件,如下图所示:
我们可以先把代码全部折叠起来,以便于了解整体的结构,如下图所示:
TIPS: -VSCode 中折叠代码的快捷键是 Ctrl + K,Ctrl + 0 (macOS:Command + K,Command + 0)
整体生成的代码其实就是一个立即执行函数,这个函数是 Webpack 工作入口(webpackBootstrap),它接收一个 modules 参数,调用时传入了一个数组。
展开这个数组,里面的元素均是参数列表相同的函数。这里的函数对应的就是我们源代码中的模块,也就是说每个模块最终被包裹到了这样一个函数中,从而实现模块私有作用域,如下图所示:
我们再来展开 Webpack 工作入口函数,如下图所示:
这个函数内部并不复杂,而且注释也很清晰,最开始定义了一个 installedModules 对象用于存放或者缓存加载过的模块。紧接着定义了一个 require 函数,顾名思义,这个函数是用来加载模块的。再往后就是在 require 函数上挂载了一些其他的数据和工具函数,这些暂时不用关心。
这个函数执行到最后调用了 require 函数,传入的模块 id 为 0,开始加载模块。模块 id 实际上就是模块数组的元素下标,也就是说这里开始加载源代码中所谓的入口模块,如下图所示:
为了更好的理解 bundle.js 的执行过程,你可以把它运行到浏览器中,然后通过 Chrome 的 Devtools 单步调试一下。调试过程我单独录制了一个视频,详情见视频(19分11秒)。
总结
最后我来总结一下本课时的重点,你也可以通过这几个重点反思一下掌握与否:
Webpack 是如何满足模块化打包需求的。
Webpack 打包的配置方式以及一个可以实现配置文件智能提示的小技巧。
Webpack 工作模式特性的作用。
通过 Webpack 打包后的结果是如何运行起来的?
# 精选评论
# **6899
老师,请问下里面的课件和源码去哪下载?
# 讲师回复
源码可以到我的 GitHub 中搜索 webpack-demo。https://github.com/zce
# *帆
老师,请问一下 vite 会取代 wepake 吗
# 讲师回复
在短期内肯定不会取代,另外其实 vite 并不是 bundleless 方案,只是开发阶段不打包,生产阶段内部使用的是 rollup。
BTW,即便是 bundleless 工具成为主流,个人认为也不一定是 vite,有可能是更专业的工具,比如 snowpack
# **明
import { Configuration } from 'webpack'/*** @type {Configuration}*/可以替换为/***@type { Import('webpack').Configuration }*/
# 讲师回复
正解,这是 JSDoc 的用法,详细可以参考:https://jsdoc.app/tags-type.html 以及 VSCode 官网:https://code.visualstudio.com/docs/languages/javascript