loader 是一个导出为一个函数的 node 模块,该函数在 loader 转换资源的时候调用。给定的函数将调用 loader模块,并通过 this 上下文访问。
单个 loader 调用
当一个loader 在资源中使用,这个loader只能传入一个参数,这个参数是一个包含资源文件内容的字符串。同步 loader可以返回一个代表模块转化后的值,loader也可以通过使用this.callback(err, value)
函数,返回任意数量的值。错误传递给 this.callback
函数,或者放入同步 loader中。
多个loader
链式调用多个 loader 时,会以相反顺序执行。
- 最后的 loader 最早调用,将会传入原始资源内容。
- 第一个 loader 最后调用,期望值传出 js 和 sourcemap(可选)
- 中间的 loader 执行时,会传入前一个 loader 传出的结果
编写 loader 注意事项
1. 简单易用
loader 应该只做单一任务,使得loader易于维护,可以在更多场景链式调用
2. 链式调用
使用loader可以链式调用的优势,写五个简单的loader实现五项任务,而不是一个loader实现五项任务。
3. 模块化
保证输出模块化,loader生成的模块于普通模块遵循相同的设计原则
4. 无状态
确保loader 在不同模块转换之间不保存状态,每次运行都独立于其他编译模块以及相同模块之前的编译结果
5. loader utilities
利用 loader-utils 包,提供非常多的有用工具,常用的工具是获取传递给 loader 的选项。schema-utils 包配合 loader-utils,用于保证 loader 选项,进行与 JSON schema结构一致性的校验。
import { getOptions } from 'loader-utils'
import validateOptions from 'schema-utils'
const schema = {
type: 'object',
properties: {
test: {
type: 'sting'
}
}
}
export default function(source) {
const options = getOptions(this)
validateOptions(schema, options, 'Example loader')
/* 对资源应用进行一些转换 */
return `export default ${ JSON.stringify(source) }`
}
6. loader 依赖
如果一个loader使用外部资源(例如,从文件系统读取),必须声明它。这些信息用于使缓存 loaders 无效,以及在观察者模式下重新编译。
import path from 'path'
export default function(source) {
var callback = this.async()
var headPath = path.resolve('header.js')
this.addDependency(headPath)
fs.readFile(headPath, 'utf-8', function(err, header) {
if (err) return callback(err)
callback(null, header + "\n" + source)
})
}
7. 模块依赖
根据模块类型,可能会有不同的模块指定依赖关系。如在 css 中,使用 @import
和url(...)
语句来声明依赖。这些模块应由模块系统解析。可以通过以下两种方式的一种来实现:
- 通过将其转化成
require
语句 - 使用
this.resolve
函数解析路径
css-loader中,将
@import
语句转化为require
其他样式文件,将url(...)
替换为require
引用文件,实现将依赖关系转化为require
声明
less-loader,无法将每个
@import
转化为require
,因为所有.less
文件中的变量和混合跟踪必须经历一次编译。因此,less-loader 将 less 编译器进行扩展,自定义路径解析逻辑,然后通过 webpack 的this.resolve
解析依赖。
如果语言只支持相对 url,如 url(file)
总是指向 ./file
,通过 ~
来指定已安装的模块(如 node_modules中的模块)。所以对于url,相当于 url('~some-library/img.jpg')
8. 通用代码
避免在 loader 处理的每个模块中生成通用代码。应该在 loader 中创建一个运行时文件,并生成 require 语句以引用该共享模块。
9. 绝对路径
不要在模块代码中插入绝对路径,当项目根路径变化时,文件绝对路径也会变化。loader-utils 中的stringifyRequest
方法,可以将绝对路径转化为相对路径。
10. 同等依赖
如果你的 loader 简单包裹另外一个包,应该把这个包作为 peerDependency 引入。这种方式允许应用程序开发者在必要的情况下,在package.json中指定所需的确定版本。
编写一个最最简单的 loader
const _loaderUtils = require('loader-utils')
module.exports = (source) => {
const options = _loaderUtils.getOptions(this);
console.log(this)
source = source.replace(/\[name\]/g, '');
return `export default ${ JSON.stringify(source) }`;
};
rules 中通过指向具体的路径使用
{
test: /\.txt$/,
use: {
loader: path.resolve(__dirname, './test/loader.js'),
options: {
name: 'Alice'
}
}
}
网友评论