Loader
loader基础
- loader的职责是单一的,只需要完成一种转换, 一个源文件需要多次转换,则需要多个loader串行处理,loader的调用是链式的, 从右往左的,第一个loader拿到源文件转换后,会将转换的结果传递给下一个loader,最后一个loader转换完成会将结果输出给webpack.开发loader只需要关注输入输出结果即可,保持单一原则
loader进阶
同步与异步
- 使用return source, this.callback(nul,source,sourceMap)的都是同步loader
- 异步loader: 使用 const callback = this.async(),在处理函数回调返回
// 异步loader示例
module.exports = function(source) {
// 告诉 Webpack 本次转换是异步的,Loader 会在 callback 中回调结果
var callback = this.async();
someAsyncOperation(source, function(err, result, sourceMaps, ast) {
// 通过 callback 返回异步执行后的结果
callback(err, result, sourceMaps, ast);
});
};
获取配置的options
- wepack提供了loader-utils包,可以获取options
const loaderUtils = require('loader-utils');
module.exports = function(source) {
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
return source;
};
返回sourceMap
- 使用this.callback(null, source, sourceMap) 第一个参数是err,没有错误的时候应为null
module.exports = function(source) {
this.callback(null, source, sourceMaps);
};
处理二进制
类似于file-loader, url-loader是处理文件,需要转换成buffer返回给webpack而不是string
module.exports = function(source) {
return source;
};
module.exports.raw = true;
module.exports.raw = true;
, 告诉webpack使用二进制处理
使用缓存加速
webpack会默认所有loader都使用缓存加速,如果需要关闭可以配置 this.cacheable(fasle)
module.exports = function(source) {
// 关闭该 Loader 的缓存功能
this.cacheable(false);
return source;
};
其他api不学了.
Plugin
- webpack 就像一条生产线,要经过一些列处理流程后才能将源文件转换成输出结果,这条生产线上的每个流程职责都是单一的,多流程之间存在依赖关系,只有完成当前流程后,才能交给下一个流程去处理,而插件就像是插入到生产线中的一个功能,在特定ed时机对生产线的资源做出处理.
- webpack这种事件流机制应用了观察者模式,依赖于tapable,webpack运行生命周期中会广播出很多事件,plugin可以监听这些事件也只需要关心这些事件,在合适的时机调用来改变输出结果.
Compiler和Compilation
开发Plugin时最常用的两个对象是Compiler和Compilation,两者是连接Plugin和webpack的桥梁.
- Compiler: 包含了webpack环境所有的配置信息(可以理解为是 class Compiler的实例),通过compiler.hooks.(webpack生命周期).tapAsync(事件名,事件回调),来绑定事件
- Compilation: 包含了当前模块资源,编译生成资源等 编写fileListPlugin示例 webpack plugin必须提供apply方法,webpack会遍历所有插件调用apply方法,将compiler实例传给插件
class FileListPlugin {
constructor({ filename }) {
this.filename = filename || 'index.md'
}
apply(compiler) {
// 文件准备好了, 进行发射前, 生成文件列表清单
compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, cb) => {
let content = `## 文件名 资源大小\r\n`
let assets = compilation.assets
Object.entries(assets).forEach(([filename, statObj]) => {
content += `- ${filename} ${statObj.size()}\r\n`
})
assets[this.filename] = {
source() {
return content
},
size() {
return content.length
}
}
compilation.assets = assets
cb()
})
}
}
module.exports = FileListPlugin