webpack-loader
Webpack Loader 的工作原理基于 模块转换链,其核心功能是将不同类型的文件(如 CSS、图片、TS)转换为 Webpack 可处理的模块。以下是其详细机制:
概念
- Loader 本质
- 一个 函数,接收源文件内容作为输入,返回转换后的内容。
- 支持链式调用,多个 loader 按顺序处理同一个文件。
- 执行顺序,从右到左或从下到上。
js
{
test: /\.scss$/,
use: [
'style-loader', // 最后执行:将 CSS 注入 DOM
'css-loader', // 解析 CSS 中的 @import 和 url()
'sass-loader' // 首先执行:将 SCSS 编译为 CSS
]
}
工作流程
- 模块请求解析
- 当 Webpack 遇到非 JS 模块导入(如 import './style.css')时,根据 module.rules 匹配对应的 loader。
- Loader 链调用
- 按配置顺序依次调用 loader,每个 loader 接收前一个 loader 的输出(或原始文件内容)。
- 结果传递
- 最后一个 loader 必须返回 JS 代码(字符串或 Buffer),因为 Webpack 只能处理 JS 模块。
- 返回的 JS 代码会被 Webpack 解析为模块,并加入依赖图。
开发要点
基本结构
js
// 同步 loader
module.exports = function(source) {
// source: 源文件内容(字符串或 Buffer)
// 转换逻辑
const result = doSomething(source);
return result; // 必须返回 JS 代码
};
// 异步 loader
module.exports = function(source) {
const callback = this.async(); // 获取异步回调
setTimeout(() => {
const result = doSomethingAsync(source);
callback(null, result); // 回调参数:(error, result)
}, 1000);
};
loader API
js
// this包含 Webpack 提供的工具和信息:
module.exports = function(source) {
// 获取 loader 配置参数
const options = this.getOptions();
// 获取文件路径
const filePath = this.resourcePath;
// 添加依赖(让 Webpack 监听文件变化)
this.addDependency('path/to/dependency');
// 返回多个结果
return this.callback(null, source, sourceMap, meta);
};
Loader 与 Plugin 的区别
特性 | Loader | Plugin |
---|---|---|
作用对象 | 单个文件 | 整个构建过程 |
核心功能 | 文件格式转换(如 SCSS → CSS) | 修改、优化或生成资源 |
执行时机 | 模块解析阶段 | 通过钩子在特定阶段触发 |
配置方式 | module.rules | plugins 数组 |
返回值 | 必须是 JS 代码 | 无(通过修改 compilation 对象 |
优化建议
使用缓存
- 使用
cache-loader
(webpack4以下) 或babel-loader
的cacheDirectory
选项开启缓存,减少重复构建时间。
js
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true // 开启缓存
}
}
}
使用 thread-loader
开启多线程处理,提升构建速度。
js
{
test: /\.js$/,
use: {
loader: 'thread-loader',
options: {
workers: 2 // 设置线程数
}
}
}
缩小处理范围
精准配置 loader 的作用范围,使用 include
和 exclude
限制处理的文件目录。
js
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'), // 只处理 src 目录
exclude: /node_modules/,
use: 'babel-loader'
}
demo
1. 字符串替换
js
module.exports = function(source) {
return source.replace(/Hello/g, 'Hi');
};
2. 带有异步操作的字符串替换
js
// replace-loader.js
const { getOptions } = require('loader-utils');
const { validate } = require('schema-utils');
// 选项验证模式
const schema = {
type: 'object',
properties: {
search: {
type: 'string',
required: true
},
replace: {
type: 'string',
required: true
},
flags: {
type: 'string',
default: 'g'
}
}
};
module.exports = function(source) {
const options = getOptions(this) || {};
validate(schema, options, { name: 'ReplaceLoader' });
const callback = this.async();
// 模拟异步操作
setTimeout(() => {
try {
const regex = new RegExp(options.search, options.flags);
const result = source.replace(regex, options.replace);
callback(null, result);
} catch (error) {
callback(error);
}
}, 100);
};
webpack常见的loader
- css-loader:解析css
- less-loader:解析less为css
- style-loader:将css注入dom中
- postcss-loader:补充css前缀
- file-loader:将文件输出到目录中,返回url
- url-loader:类似于file-loader,输出base64
- babel-loader:将es6转为es6
- ts-loader:转换ts
- vue-loader:转换vue文件