Webpack 知识点总结
webpack随着现代前端开发的规模日益庞大,几乎已经不能抛开诸如 React 或 Vue 等前端开发框架来独立开发了,这些框架很大程度上的提高了我们的开发效率,但是框架中所使用特殊文件,比如
jsx
、vue
、scss
或less
等,浏览器是无法直接识别的,必须经过编译后才能在浏览器中使用。于是在众多的前端工程化工具中,Webpack 脱颖而出成为了当今最流行的前端构建工具。 [1]
Webpack 配置模板:
// webpack.config.js
const path = require('path');
module.exports = {
entry: "./app/entry", // string | object | array
// Webpack打包的入口
output: { // 定义webpack如何输出的选项
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
filename: "[chunkhash].js", // string
// 「入口(entry chunk)」文件命名模版
publicPath: "/assets/", // string
// 构建文件的输出目录
/* 其它高级配置 */
},
module: { // 模块相关配置
rules: [ // 配置模块loaders,解析规则
{
test: /\.jsx?$/, // RegExp | string
include: [ // 和test一样,必须匹配选项
path.resolve(__dirname, "app")
],
exclude: [ // 必不匹配选项(优先级高于test和include)
path.resolve(__dirname, "app/demo-files")
],
loader: "babel-loader", // 模块上下文解析
options: { // loader的可选项
presets: ["es2015"]
},
},
},
resolve: { // 解析模块的可选项
modules: [ // 模块的查找目录
"node_modules",
path.resolve(__dirname, "app")
],
extensions: [".js", ".json", ".jsx", ".css"], // 用到的文件的扩展
alias: { // 模块别名列表
"module": "new-module"
},
},
devtool: "source-map", // enum
// 为浏览器开发者工具添加元数据增强调试
plugins: [ // 附加插件列表
// ...
],
}
Webpack 的运行流程是怎样的?
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:[2]
-
初始化参数:从配置文件
webpack.config.js
和命令行中读取与合并参数,初始化本次构建的配置参数,得出最终的参数; -
开始编译:用上一步得到的参数初始化
Compiler
对象,加载配置文件的所有plugin
,执行对象的 run 方法开始执行编译; -
确定入口:根据配置中的
entry
找出所有的入口文件,递归遍历所有的入口文件; -
编译模块:从入口文件出发,调用所有配置的
loader
对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。 -
输出资源:所有文件的编译及转化都已经完成,也就是最终输出的资源,其中包括即将输出的资源、代码块
Chunk
等等信息。
运行流程图:[3]
Webpack 运行流程图Webpack 的 Loader 是什么?
Webpack
只能理解 JavaScript
和 JSON
文件,这是 Webpack
开箱可用的自带能力。loader
可以让 Webpack
能够去处理其他类型的文件,比如 .scss
和 .ts
,并将它们转换为有效的功能离散的 chunk
文件以供应用程序使用,以及被添加到依赖图中,也可将内联图像转换为 data URL
。简单来说,loader
可以将一段代码转换成另一端代码,通常用来将一段特殊代码转换成一段浏览器可识别的代码。
loader
从下到上地取值(evaluate)/执行(execute),也就是是从后往前执行。在下面的示例中,从 ts-loader
开始执行,然后继续执行 css-loader
,最后以 raw-loader
为结束。loader
有两个属性:test
,正则表达式,用于识别出哪些文件会被转换,use
定义在进行转换时应该使用哪个 loader
,可以是字符串、数组和对象。[4]
// webpack.config.js
module.exports = {
output: {
filename: "my-first-webpack.bundle.js",
},
module: {
rules: [
{ test: /\.txt$/, use: "raw-loader" }, // 通过 npm 安装
{
test: /\\.css$/,
use: [
{
loader: "css-loader", // 通过 npm 安装
options: { modules: true },
},
],
},
{ test: /\\.ts$/, use: "ts-loader" }, // 通过 npm 安装
],
},
};
有哪些常见的 Loader ?👉 Loaders
-
raw-loader
:加载文件原始内容(utf-8) -
file-loader
:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体) -
url-loader
:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体) -
source-map-loader
:加载额外的 Source Map 文件,以方便断点调试 -
svg-inline-loader
:将压缩后的 SVG 内容注入代码中 -
image-loader
:加载并且压缩图片文件 -
json-loader
加载 JSON 文件(默认包含) -
handlebars-loader
: 将 Handlebars 模版编译成函数并返回 -
babel-loader
:把 ES6 转换成 ES5 -
ts-loader
: 将 TypeScript 转换成 JavaScript -
awesome-typescript-loader
:将 TypeScript 转换成 JavaScript,性能优于 ts-loader -
sass-loader
:将SCSS/SASS代码转换成CSS -
css-loader
:加载 CSS,支持模块化、压缩、文件导入等特性 -
style-loader
:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS -
postcss-loader
:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀 -
eslint-loader
:通过 ESLint 检查 JavaScript 代码 -
tslint-loader
:通过 TSLint检查 TypeScript 代码 -
mocha-loader
:加载 Mocha 测试用例的代码 -
coverjs-loader
:计算测试的覆盖率 -
vue-loader
:加载 Vue.js 单文件组件 -
i18n-loader
: 国际化 -
cache-loader
: 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里
Webpack 的 Plugin 是什么?
loader
用于转换某些类型的模块,而 plugin
(插件)则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。想要使用一个 plugin
,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过 option
自定义,也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建一个插件实例。[5]
// webpack.config.js
const webpack = require('webpack'); // 用于访问内置插件
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
module.exports = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
有哪些常见的 Plugin ?Plugins
-
define-plugin
:定义环境变量 (Webpack4 之后指定 mode 会自动配置) -
ignore-plugin
:忽略部分文件 -
html-webpack-plugin
:简化 HTML 文件创建 (依赖于 html-loader) -
web-webpack-plugin
:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用 -
uglifyjs-webpack-plugin
:不支持 ES6 压缩 (Webpack4 以前) -
terser-webpack-plugin
: 支持压缩 ES6 (Webpack4) -
webpack-parallel-uglify-plugin
: 多进程执行代码压缩,提升构建速度 -
mini-css-extract-plugin
: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin) -
serviceworker-webpack-plugin
:为网页应用增加离线缓存功能 -
clean-webpack-plugin
: 目录清理 -
ModuleConcatenationPlugin:
开启 Scope Hoisting -
speed-measure-webpack-plugin
: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时) -
webpack-bundle-analyzer
: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)
Webpack 的热更新原理是怎样的?
Webpack
的热更新又称热替换(Hot Module Replacement
),缩写为 HMR
。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
-
Webpack
通过Watch
模式可以侦听文件的变化,当文件发生改变时,会根据配置进行重新编译(Compile
),并将编译后的代码保存在内存中。 -
webpack-dev-server
也会对文件变化进行监控(需要配置devServer.watchContentBase = true
),但不会进行重新编译,而是监听这些配置文件中静态文件的变化,变化后会通知浏览器进行直接刷新,而不是HMR
。 - 在浏览器和服务端之间有一个通过
SocketJs
建立的websocket
长连接。webpack-dev-server
会将Webpack
编译打包时的各个阶段的状态信息和 hash 值一并告知webpack-dev-server/client
(位于浏览器端)。 - 但是
webpack-dev-server/client
并不能够请求更新的代码,而是把这些工作交给了webpack/hot/dev-server
,webpack/hot/dev-server
的工作就是根据webpack-dev-server/client
传来的信息以及dev-server
的配置决定是刷新浏览器还是HMR
。 -
HotModuleReplacement.runtime
是客户端HMR
的中枢,它接收到webpack/hot/dev-server
传递的新模块的 hash 值,通过JsonpMainTemplate.runtime
向webpack-dev-server
发送Ajax
请求获取到返回的Json
,该Json
包含了所有要更新的模块的 hash 值,之后通过Jsonp
请求,获取到最新的模块代码。 - 接下来,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
- 如果
HMR
失败,则通过刷新浏览器来获取最新打包代码。