webpack的loader和plugin的区别

webpack 的常见配置

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
30
31
32
33
34
35
const webpack = require("webpack");
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// 入口文件
entry: {
app: path.join(__dirname, "../src/js/index.js"),
},
// 输出文件
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/",
},
// loader配置
module: {
rules: [
{
test: /\.scss/,
use: ["style-loader", "css-loader"],
},
// ......
],
},
// plugins配置
plugins: [
// 重新创建html文件
new HtmlWebpackPlugin({
title: "首页",
filename: "index.html",
template: path.resolve(__dirname, "../src/index.html"),
}),
// ......
],
};

webpack 的打包原理

  1. 识别入口文件
  2. 通过逐层识别模块依赖(Commonjs、amd 或者 es6 的 import,webpack 都会对其进行分析,来获取代码的依赖)
  3. webpack 做的就是分析代码,转换代码,编译代码,输出代码
  4. 最终形成打包后的代码

什么是 loader

loader 是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中

  1. 处理一个文件可以使用多个 loader,loader 的执行顺序和配置中的顺序是相反的,即最后一个 loader 最先执行,第一个 loader 最后执行
  2. 第一个执行的 loader 接收源文件内容作为参数,其它 loader 接收前一个执行的 loader 的返回值作为参数,最后执行的 loader 会返回此模块的 JavaScript 源码

loader 的使用方式

一般 loader 的使用方式分为三种:

在配置文件 webpack.config.js 中配置

1
2
3
4
5
6
7
8
9
10
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: "raw-loader"
}
]
}
};

通过命令行参数方式

1
webpack --module-bind 'txt=raw-loader'

通过内联使用

1
import txt from "raw-loader!./file.txt";

webpack 常用的 loader

作用loader
处理样式,转成 cssstyle-loader、css-loader、less-loader、sass-loader
加载图片和字体等文件raw-loader、file-loader 、url-loader
加载数据 xml 和 csvcsv-loader xml-loader
编译babel-loader、coffee-loader 、ts-loader
校验测试mocha-loader、jshint-loader 、eslint-loader
读取 html 生成模板字符串html-loader
将 js 模块暴露到全局expose-loader
将第三方库注入到模块中import-loader

css-loader、sass-loader 与 style-loader

  • css-loader 处理其中的@import 和 url()。
  • sass-loader 转化 sass 为 css 文件,并且包一层 module.exports 成为一个 js module。
  • style-loader 将创建一个 style 标签将 css 文件嵌入到 html 中。

file-loader、url-loader 与 raw-loader

  • file-loader 可以复制和放置资源位置,并可以指定文件名模板,用 hash 命名更好利用缓存。
  • url-loader 可以将小于配置 limit 大小的文件转换成内敛 Data Url 的方式,减少请求。
  • raw-loader 可以将文件以字符串的形式返回

什么是 plugin

在 webpack 运行的生命周期中会广播出许多事件,plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyPlugin {
constructor(options) {
console.log("MyPlugin constructor:", options);
}
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
console.log("MyPlugin");
});
}
}
module.exports = MyPlugin;

// webpack.config.js 配置:
module.exports = {
// ...
plugins: [new MyPlugin({ param: "my plugin" })],
};

使用该 plugin 后,执行的顺序:

  1. webpack 启动后,在读取配置的过程中会执行 new MyPlugin(options)初始化一个 MyPlugin 获取其实例
  2. 在初始化 compiler 对象后,就会通过 compiler.plugin(事件名称,回调函数)监听到 webpack 广播出来的事件
  3. 并且可以通过 compiler 对象去操作 webpack

webpack 常用的 plugin

Plugin作用
UglifyJsPluginwebpack 内置,压缩和混淆代码
optimize-css-assets-webpack-plugin不同组件中重复的 css 可以快速去重
CommonsChunkPluginwebpack 内置,提高打包效率,将第三方库和业务代码分开打包,多个 html 共用一个 js 文件(chunk)
ProvidePlugin自动加载模块,代替 require 和 import
DefinePlugin编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用
NormalModuleReplacementPlugin匹配 resourceRegExp,替换为 newResource
ContextReplacementPlugin替换上下文的插件
IgnorePlugin不打包匹配文件
ResolverPlugin替换上下文的插件
ContextReplacementPlugin替换上下文的插件
html-webpack-plugin可以根据模板自动生成 html 代码,并自动引用 css 和 js 文件
extract-text-webpack-plugin将 js 文件中引用的样式单独抽离成 css 文件,非内联
HotModuleReplacementPlugin热更新
Externals常用于引入外链作为 library
DllPlugin、DllReferencePlugin前置第三方包的构建,只构建业务代码,同时能解决 Externals 多次引用问题
compression-webpack-plugin生产环境可采用 gzip 压缩 JS 和 CSS
happypack通过多进程模型,来加速代码构建
PrefetchPlugin预加载的插件,提高性能
clean-webpack-plugin清理每次打包下没有使用的文件
webpack-bundle-analyzer一个 webpack 的 bundle 文件分析工具,将 bundle 文件以可交互缩放的 treemap 的形式展示

loader 和 plugin 的区别

Loader

Loader 直译为”加载器”。Webpack 将一切文件视为模块,但是 webpack 原生是只能解析 js 文件,如果想将其他文件也打包的话,就会用到 loader。 所以 Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力。

对于 loader,它是一个转换器,将 A 文件进行编译形成 B 文件,这里操作的是文件,比如将 A.scss 转换为 A.css,单纯的文件转换过程

Plugin

Plugin 直译为”插件”。Plugin 可以扩展 webpack 的功能,让 webpack 具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

plugin 是一个扩展器,它丰富了 webpack 本身,针对是 loader 结束后,webpack 打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听 webpack 打包过程中的某些节点,执行广泛的任务

如何编写 loader 或 plugin

编写思路

  1. 编写 Loader 时要遵循单一原则,每个 Loader 只做一种”转义”工作, 每个 Loader 的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可 以调用 this.callback()方法,将内容返回给 webpack,还可以通过 this.async()生成一个 callback 函数,再用这个 callback 将处理后的内容输出出去,此外 webpack 还为开发者准备了开发 loader 的工具函数集——loader-utils
  2. 相对于 Loader 而言,Plugin 的编写就灵活了许多, webpack 在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 webpack 提供的 API 改变输出结果

编写注意事项

  1. Loader 支持链式调用,所以开发上需要严格遵循“单一职责”,每个 Loader 只负责自己需要负责的事情
  2. Loader 运行在 node.js 中,我们可以调用任意 node.js 自带的 API 或者安装 第三方模块进行调用
  3. webpack 传给 Loader 的原内容都是 UTF-8 格式编码的字符串,当某些场景下
    Loader 处理二进制文件时,需要通过 exports.raw = true 告诉 webpack 该 Loader 是否需要
    二进制数据
  4. 尽可能的异步化 Loader,如果计算量很小,同步也可以
  5. Loader 是无状态的,我们不应该在 Loader 中保留状态
  6. 使用 loader-utils 和 schema-utils 为我们提供的实用工具
  7. 加载本地 Loader 方法