Skip to main content

介绍

构建 Build

构建其实是工程化自动化的思想在前端开发中的体现,把一系列流程用代码实现,让代码自动化地执行一系列复杂的流程。常见功能有:

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  • 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

依赖于模块

在一个项目中,Webpack 包含 输入entry)和 输出output)两个部分。打包过程从用户定义的 模块 开始,在模块中又可以通过 导入requireimport)指向其他模块。

当我们使用 Webpack 打包项目时,它会遍历导入,构建项目的 依赖关系图,然后根据配置生成 输出。此外,还可以定义 拆分点,以在项目代码本身内创建单独的包。

note

依赖图 是描述节点如何相互关联的有向图。我们通过依赖图描述了文件之间的引用(requireimport)关系。Webpack 在不执行源代码的情况下静态遍历这些源代码,以生成创建 bundle 所需的 依赖图

Webpack 支持开箱即用的 ES2015,CommonJS 和 AMD 模块格式。loader 机制也适用于 CSS,通过 css-loader,我们可以使用 @importurl() 来导入 CSS 文件。我们还可以找到特定任务的插件,例如压缩,国际化,HMR 等。

执行流程

Webpack 通常从 JavaScript 模块开始遍历。在此过程中,Webpack 会根据 loaders 配置验证模块是否匹配,并且会按照配置来转换匹配的模块。

解析过程

entry 本身就是一个模块。当 Webpack 遇到一个 entry 时,Webpack 会尝试使用 entryresolve 配置将 entry 与文件系统中的文件匹配。除了 node_modules 之外,我们还可以告诉 Webpack 对特定目录执行查找。也可以调整 Webpack 匹配文件扩展名的方式,并且可以为目录定义特定的别名。

如果解析失败,Webpack 会引发运行时错误。如果 Webpack 正确解析文件,Webpack 将根据 loader 的定义对匹配的文件执行处理。每个 loader 对模块内容应用特定的转换。

loader 的执行也具有相同的解析过程,Webpack 允许在确定应使用哪个 loader 时应用类似的逻辑。由于这个原因,Webpack 必须预先解析 loader 的配置。如果 Webpack 无法查找到 loader 程序,则会引发运行时错误。

tip

通过 enhanced-resolve 包,Webpack 可以异步加载 loader。

处理各种文件类型

Webpack 在构造依赖图时解析它遇到的每个模块。如果 entry 包含依赖项,则将针对每个依赖项递归执行该过程,直到遍历完成为止。Webpack 可以针对任何文件类型执行此过程,这与 Babel 或 Sass 编译器等专用工具不同。

Webpack 使我们可以控制如何处理遇到的不同类型的资源。例如,我们可以决定将资源 内联 到 JavaScript 代码中以避免被处理。Webpack 还允许我们使用 CSS 模块等技术将样式与组件结合,并避免一些 CSS 兼容性问题。这种灵活性使 Webpack 非常有价值。

虽然 Webpack 主要用于打包 JavaScript,但它可以捕获图像或字体等资源,并为它们发出单独的文件。entry 只是打包过程的起点。Webpack 打包出的内容完全取决于我们配置它的方式。

转换过程

假设所有的 loader 都被找到,Webpack 将从下到上从右到左的对 loader 进行匹配,同时依次通过每个匹配 loader 来运行模块。因此,我们将获得 Webpack 在打包结果中注入的输出。

如果所有 loader 执行都没有发生运行时错误,则 Webpack 将源代码包含在最后一个包中。plugins 允许我们在打包过程的不同阶段拦截 运行时事件

虽然 loader 可以做很多事情,但它们不能为高级任务提供足够的动力。plugins 可以拦截 Webpack 提供的 运行时事件。一个很好的例子是包内容的提取,当 MiniCssExtractPlugin 与 loader 一起使用时,从包中抽出 CSS 并将其提取到单独的文件中。如果没有这一步,CSS 将在生成的 JavaScript 中内联,因为 Webpack 默认将所有代码视为 JavaScript。

输出文件

转换完每个模块后,Webpack 会写入 output。output 包括一个启动脚本,其中包含一个描述如何在浏览器中开始执行结果的 manifest。如本书后面所述,可以将 manifest 提取到自己的文件中。output 根据我们使用的构建目标而有所不同(Web 不是唯一选项)。

此外,我们还可以分割打包 Bundle Splitting懒加载 Code Splitting

Webpack Config Example

Webpack 的核心依赖于配置,以下是根据官方 Webpack 教程改编的示例配置:

webpack.config.js
const webpack = require('webpack');

module.exports = {
// 这里是打包入口
entry: {
app: './src/index.js', // 'app' 是最后输出的文件名,将会替换下面的'[name].js' 的 'name' 部分
},

// 这里是打包出口
output: {
// 输出到同样的文件夹
path: __dirname,

// 通过某种模式使用入口文件的名字定义打包文件名称
filename: '[name].js',
},

// 对于每一个文件导入提供一些解析规则
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},

// 定义一些插件,来处理转换过程外额外要做的事情
plugins: [new webpack.DefinePlugin({ ... })],

// 调整模块解析的算法
resolve: {
alias: { ... }
}
};

Webpack 的配置有时会让人感觉有点不透明,因为配置文件可能看起来是单一庞杂的。除非你知道背后的想法,否则很难理解 Webpack 在做什么。提供条理清晰的配置方法是本教程存在的主要目的之一。

参考资料

  1. Webpack Guidebook: 构建工具, by tsejx
  2. 玩转 webpack, by 程柳锋
  3. 带你深度解锁 Webpack 系列, by 刘小夕