Skip to main content

介绍

构建 Build​

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

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

依赖于模块​

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

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

note

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

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

执行流程​

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

解析过程​

entry 本身就是一个模块。当 Webpack 遇到一个 entry 时,Webpack 会尝试使用 entry 的 resolve 配置将 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 刘小夕