这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
Webpack Loader
loader 用于对模块的源代码进行转换
配置
- 单个 loader,你可以简单通过在 rule 对象设置
path.resolve指向这个本地文件 - 多个 loaders,你可以使用
resolveLoader.modules配置 - 测试的时候可以使用
npm link - 当链式调用多个 loader 的时候,请记住它们会以相反的顺序执行。取决于数组写法格式,从右向左或者从下向上执行
- 最后的 loader 最早调用,将会传入原始资源内容。
- 第一个 loader 最后调用,期望值是传出 JavaScript 和 source map(可选)。
- 中间的 loader 执行时,会传入前一个 loader 传出的结果。
demo: 将 var 关键词替换为 const
# loader.js
module.exports = function (source) {
return source.replace(/var/g, 'const')
}
# index.js
function test() {
var a = 1;
var b = 2;
var c = 3;
console.log(a, b, c);
}
test()
# wepback.config.js
const path = require('path')
module.exports = {
mode: 'development',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.js$/,
use: [
{
loader: path.resolve('./src/loader1.js'),
},
]
}
]
},
}
# `npm run build`,得到打包文件 `bundle.js`
用法准则
- 简单易用。
- 使用链式传递。
- 模块化的输出。
- 确保无状态。
- 使用 loader utilities。
- 记录 loader 的依赖。
- 解析模块依赖关系。
- 提取通用代码。
- 避免绝对路径。
- 使用 peer dependencies。
简单
loaders 应该只做单一任务。这不仅使每个 loader 易维护,也可以在更多场景链式调用。
链式
利用 loader 可以链式调用的优势。写五个简单的 loader 实现五项任务,而不是一个 loader 实现五项任务;
loader 可以被链式调用意味着不一定要输出 JavaScript。只要下一个 loader 可以处理这个输出,这个 loader 就可以返回任意类型的模块
无状态
确保 loader 在不同模块转换之间不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。
loader 工具库
充分利用
loader-utils包。它提供了许多有用的工具,但最常用的一种工具是获取传递给 loader 的选项。schema-utils包配合loader-utils,用于保证 loader 选项,进行与 JSON Schema 结构一致的校验。这里有一个简单使用两者的例子:
loader.js
import { getOptions } from 'loader-utils';
import validateOptions from 'schema-utils';
const schema = {
type: 'object',
properties: {
test: {
type: 'string'
}
}
}
export default function(source) {
const options = getOptions(this);
validateOptions(schema, options, 'Example Loader');
// 对资源应用一些转换……
return `export default ${ JSON.stringify(source) }`;
};
loader 依赖
如果一个 loader 使用外部资源(例如,从文件系统读取),必须声明它。这些信息用于使缓存 loaders 无效,以及在观察模式(watch mode)下重编译。下面是一个简单示例,说明如何使用
addDependency方法实现上述声明:
loader.js
import path from 'path';
export default function(source) {
var callback = this.async();
var headerPath = path.resolve('header.js');
this.addDependency(headerPath);
fs.readFile(headerPath, 'utf-8', function(err, header) {
if(err) return callback(err);
callback(null, header + "\n" + source);
});
};
模块依赖
根据模块类型,可能会有不同的模式指定依赖关系。例如在 CSS 中,使用
@import和url(...)语句来声明依赖。这些依赖关系应该由模块系统解析。
可以通过以下两种方式中的一种来实现:
- 通过把它们转化成
require语句。 - 使用
this.resolve函数解析路径
通用代码
避免在 loader 处理的每个模块中生成通用代码。相反,你应该在 loader 中创建一个运行时文件,并生成
require语句以引用该共享模块。
绝对路径
不要在模块代码中插入绝对路径,因为当项目根路径变化时,文件绝对路径也会变化。
loader-utils中的stringifyRequest方法,可以将绝对路径转化为相对路径。
同等依赖(Peer Dependencies)
如果你的 loader 简单包裹另外一个包,你应该把这个包作为一个
peerDependency引入。这种方式允许应用程序开发者在必要情况下,在package.json中指定所需的确定版本
测试
当你遵循上面的用法准则编写了一个 loader,并且可以在本地运行。下一步该做什么呢?让我们用一个简单的单元测试,来保证 loader 能够按照我们预期的方式正确运行。我们将使用 Jest 框架。然后还需要安装
babel-jest和允许我们使用import/export和async/await的一些预设环境(presets)。让我们开始安装,并且将这些依赖保存为devDependencies:
npm install --save-dev jest babel-jest babel-preset-env
.babelrc
{
"presets": [[
"env",
{
"targets": {
"node": "4"
}
}
]]
仅供学习参考