webpack基础进阶和原理剖析

webpack官方中文文档

webpack 配置中的 loader 和 plugin 的区别

在 Webpack 中,loaderplugin 是实现构建能力的核心机制,但它们的作用和工作方式有本质区别:

一、Loader(加载器):处理非 JS 模块的“翻译官”

Webpack 本质上只能识别 JavaScript 和 JSON 文件,**loader 的核心作用是将非 JS 模块(如 CSS、图片、TS 等)转换为 Webpack 可处理的模块
**,从而纳入构建流程。

1. 工作原理

  • 执行时机:在 Webpack 解析代码的过程中(模块打包阶段),当遇到非 JS 模块时触发。
  • 执行顺序:从右到左(或从下到上)链式调用。例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 先执行 sass-loader 处理 SASS,再用 css-loader 处理 CSS,最后用 style-loader 注入到 DOM
    module: {
    rules: [
    {
    test: /\.s[ac]ss$/i,
    use: ['style-loader', 'css-loader', 'sass-loader']
    }
    ]
    }
  • 输入输出:接收源文件内容作为参数,返回处理后的内容(通常是 JS 代码)。

2. 常见场景

  • 语言转换babel-loader 将 ES6+ 转换为 ES5;ts-loader 将 TypeScript 转换为 JS。
  • 样式处理css-loader 解析 CSS 中的 @importurl()style-loader 将 CSS 注入到页面 <style> 标签;
    sass-loader 编译 SASS/SCSS 为 CSS。
  • 资源处理file-loader 处理图片、字体等资源并输出到指定目录;url-loader 在文件体积较小时将资源转为 Base64 嵌入代码。

二、Plugin(插件):扩展构建流程的“功能增强器”

Plugin 用于扩展 Webpack 的功能,可以介入构建的整个生命周期(从初始化到输出产物),实现loader
无法完成的复杂操作(如打包优化、环境变量注入、资源管理等)。

1. 工作原理

  • 基于钩子机制:Webpack 在构建过程中会触发一系列生命周期钩子(如 entryOptioncompileemit 等),plugin
    通过注册这些钩子,在特定时机执行自定义逻辑。
  • 灵活性高:可以修改输出资源、调整构建流程、生成额外文件等。

2. 常见场景

  • 优化输出TerserPlugin 压缩 JS 代码;MiniCssExtractPlugin 将 CSS 提取为单独文件(替代 style-loader)。
  • 环境配置DefinePlugin 注入全局环境变量(如 process.env.NODE_ENV)。
  • 性能分析BundleAnalyzerPlugin 可视化打包产物,分析体积过大的原因。
  • 资源处理CopyWebpackPlugin 复制静态资源到输出目录;HtmlWebpackPlugin 自动生成 HTML 并引入打包后的资源。

三、核心区别

维度 Loader Plugin
作用对象 针对模块文件(如 CSS、TS) 针对整个构建流程
功能范围 仅处理文件转换(输入→输出内容) 可扩展任意构建功能(优化、注入、生成等)
用法 module.rules 中配置 plugins 数组中实例化
执行时机 模块解析阶段 整个生命周期(从初始化到输出)

四、总结

  • Loader 解决“Webpack 不能识别非 JS 模块”的问题,专注于文件内容的转换。
  • Plugin 解决“扩展 Webpack 功能”的问题,通过钩子机制介入构建全流程,实现更复杂的需求。

两者相辅相成:Loader 负责“翻译”各类资源,Plugin 负责“增强”构建过程,共同构成了 Webpack 灵活强大的构建能力。

自定义 loader

写一个 uppercase-loader,实现将文件内容转换为大写

loader 本质是函数,做编译的增强

1
2
3
4
// /loaders/uppercase-loader.js
export default function uppercaseLoader (source) {
return source.toUpperCase()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// webpack.config.dev.js
export default {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
entry: './src/index.js',
module:{
rules:[
{
test: /\.js$/,
use:['./loaders/uppercase-loader.js']
}
]
}
}

自定义 plugin

plugin 本质是类,做构建的增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 产物统计
// /plugins/AssetsMapPlugin.js

export default class AssetsMapPlugin {
constructor(options){

}
apply(compiler){
// compiler 是 webpack 实例,通过 compiler 可以访问到 webpack 所有的配置和插件
compiler.hooks.initialize.tap('AssetsMapPlugin', (compilation) => {
// compilation 是当前打包的上下文,通过 compilation 可以访问到当前打包的所有资源
console.log(compilation.assets)
})
compiler.hooks.run.tapAsync('AssetsMapPlugin', (compilation, next) => {
// compilation 是当前打包的上下文,通过 compilation 可以访问到当前打包的所有资源
console.log(compilation.assets)
let fileList = ""
for (const fileName in compilation.assets) {
fileList += fileName + '\n'
}
compilation.assets['file-list.md'] = {
source: () => fileList,
size: () => fileList.length
}
next() // 必须调用 next 才能继续打包,权限交给下一个插件
})
}
}

1
2
3
4
5
6
7
8
// webpack.config.dev.js

export default {
plugins: [
new AssetsMapPlugin()
]
}

会输出一个 file-list.md 文件,内容是当前打包的所有资源文件名

webpack 原理核心

  1. compiler
  2. compilation
  3. module
  4. chunk
  5. dependency
  6. resolver
  7. loader
  8. plugin

webpack基础进阶和原理剖析
https://zouhualu.github.io/20240509/webpack基础进阶和原理剖析/
作者
花鹿
发布于
2024年5月9日
许可协议