# 编写一个loader
定义: loader 只是一个导出为函数的 JavaScript 模块。
一个简单的loader即可以通过编写一个简单的JavaScript模块,并将模块导出即可。即如下, 接受一个source当前源码,并返回新的源码即可。
module.exports = function(source) {
return source
}
# loader的执行顺序:由后向前
其中,多个loader的执行顺序是由后向前执行,例如:
module.exports = {
module: {
rules:[{
test:/.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
}]
}
}
运行代码:https://github.com/PCAaron/blogCode/tree/master/webpack/loader-order
# loader的开发和调试:loader-runner
loader-runner运行在不安装webpack的情况下运行loaders,进行loader的开发和调试。其中,在webpack中, 作为webpack的依赖,webpack使用它执行loader。
- loader-runner的使用:
import { runLoaders } from 'loader-runner'
runLoaders({
// String: 资源的绝对路径(可以增加查询字符串)
resource: "/abs/path/to/file.txt?query",
// String[]: loader 的绝对路径(可以增加查询字符串)
loaders: ["/abs/path/to/loader.js?query"],
// 基础上下文之外的额外 loader 上下文
context: { minimize: true },
// 读取资源的函数
readResource: fs.readFile.bind(fs)
}, function(err, result) {
// err: Error?
// result.result: Buffer | String
// result.resourceBuffer: Buffer
// result.cacheable: Bool 结果是否需要缓存
// result.fileDependencies: String[] 解析结果需要依赖的文件
// result.contextDependencies: String[] 上下文依赖
})
- 调试raw-loader
实现开发一个loader,将文件内容转换为模块:处理特殊字符转义并以模块方式导出。
//src/raw-loader
module.exports = function (source) {
const json = JSON.stringify(source)
json.replace(/\u2028/g,'\\u2028')
.replace(/\u2029/g,'\\u2029')
return `export default ${json}`
}
新建run-loader.js调试raw-loader。
//run-loader.js
const fs = require('fs')
const path = require('path')
const { runLoaders } = require('loader-runner')
runLoaders({
resource: path.join(__dirname, './src/demo.txt'),
loaders: [
path.join(__dirname, './src/raw-loader.js')
],
context: { minimize: true },
readResource: fs.readFile.bind(fs)
},(err, result) => {
err ? console.error(err) : console.log(result)
})
线上代码:https://github.com/PCAaron/blogCode/tree/master/webpack/raw-loader
# loader-utils:loader的参数获取
通过loader-utils的getOptions方法可以获取到loader传递过来的参数。
//run-loader.js
runLoaders({
loaders: [
{
loader: path.join(__dirname, './src/raw-loader.js'),
options: {
name: 'test'
}
}
],
...
})
//src/raw-loader.js
const loaderUtils = require('loader-utils')
module.exports = function (source) {
const { name } = loaderUtils.getOptions(this)
console.log(name)
}
# loader API
loader-runner提供一些API给给定函数调用。
# loader的同步处理
实现同步的loader的异常处理:
- 可通过在loader内直接抛出错误throw new Error(err);
- 亦可以通过loader上下文的回调函数this.callback传递错误并返回回调内容
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap, // 必须是一个可以被这个模块解析的 source map
meta?: any // 会被 webpack 忽略,可以是任何东西(例如一些元数据)
);
//src/raw-loader.js
module.exports = function (source) {
const json = JSON.stringify(source)
...
//return `export default ${json}`
// throw new Error('err')
this.callback(null, json)
}
# loader的异步处理
通过loader上下文函数this.async来返回一个异步函数,其中,第一个参数为Error,第二个参数为回调内容。
// src/raw-loader.js
const path = require('path')
const fs = require('fs')
module.exports = function (source) {
// 异步方式
const callback = this.async()
...
fs.readFile(path.join(__dirname, './async.txt'), 'utf-8', (err, data) => {
if(err) {
callback(err, '')
return
}
callback(null, data)
})
}
# loader中使用缓存
在webpack中,会默认开启loader缓存,如果不需要缓存则可以通过this.cacheable(false)关闭。
另外,缓存使用的条件需要在,loader的结果在相同的输入下有确定的输出,并且在有依赖的loader下是
无法使用缓存的。
# this.emitFile:文件输出
通过loader上下文函数this.emitFile可以将内容输出到指定目录。
# 推荐阅读
编写一个loader:https://www.webpackjs.com/contribute/writing-a-loader/