说明
webpack默认只能处理JavaScript和JSON文件,但前端项目中,文件类型多种多样.比如.vue、.ts、图片、.css等,这就需要各种各样的loader来处理各种文件,最后交给webpack的是js模块(通过loader来弥补webpack处理其他文件的能力)
loader的本质
loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。
//最简单的一个loader,不处理任何文件,直接返回资源
function loader(source) {
// 一个包含资源文件内容的字符串
return source;
}
module.exports = loader;
loader 在webpack中的调用时机
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 :
1.初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
2.开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。
3.确定入口:根据配置中的 entry 找出所有的入口文件。
4.编译模块:从入口文件出发,调用所有配置的 loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
5.完成模块编译:在经过第 4 步使用Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
6.输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
7.输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在webpack中使用
webpack.config.js
module.exports = {
//...
module: {
rules: [
{
test: /\.less/,
use: ['style-loader','less-loader'],
},
],
},
};
上面这段代码的意思是匹配后缀为.less的文件依次应用 less-loader=>style-loader
less-loader 的源码的大概样子
// less-loader.js
const less = require('less');
module.exports = function(source) {
// this.async()表示该loader为异步loader
const callback = this.async();
// less 为node的一个模块 该模块的render函数接收两个参数,
// source为less文件的字符串,cd()函数为处理完的回调函数
// render的作用就是把less变为css 也就是一个函数输入less输出css
less.render(source, function(err, data) {
callback(err, data.css)
})
}
// style-loader.js
module.exports = function(source) {
// source为已经转成的css代码
// 1.动态的生成style标签
let style = document.createElement('style');
// 2.将css代码插入到style标签中
style.innerHTML = JSON.stringify(source);
// 3.将style标签插入到head标签中
document.head.appendChild(style);
let content= `
let style = document.createElement('style');
style.innerHTML = ${JSON.stringify(source)};
document.head.appendChild(style);
`;
return content;
}
自己实现一个webpack loader
// replace-loader.js (将代码中的console.log(xxx)替换为控字符串)
module.export = function(source) {
const result = source.replace(/console.log(.*)/g, '');
return result;
}
loader在webpack中的作用
最后执行的loader(例如上面例子中的style-loader):让webpack能够处理非js文件(将非js模块转化为js模块)
中间执行的loader 可以返回一下一个loader能识别的字符格式就可以
loader的分类与用法
loader 分类
同步:
function loader(source) {
// 一个包含资源文件内容的字符串
return source;
}
module.exports = loader;
异步:
module.exports = function(source) {
// 告诉 Webpack 本次转换是异步的,Loader 会在 callback 中回调结果
var callback = this.async();
//doSomething 异步操作
doSomething(source, function(err, result, sourceMaps, ast) {
// 通过 callback 返回异步执行后的结果
callback(err, result, sourceMaps, ast);
});
};
loader返回其他的结果
module.exports = function(source) {
// 通过 this.callback 告诉 Webpack 返回的结果
this.callback(null, source, sourceMaps);
// 当你使用 this.callback 返回内容时,该 Loader 必须返回 undefined,
return;
};
/**
this.callback(
// 当无法转换原内容时,给 Webpack 返回一个 Error
err: Error | null,
// 原内容转换后的内容
content: string | Buffer,
// 用于把转换后的内容得出原内容的 Source Map,方便调试
sourceMap?: SourceMap,
// 如果本次转换为原内容生成了 AST 语法树,可以把这个 AST 返回,
// 以方便之后需要 AST 的 Loader 复用该 AST,以避免重复生成 AST,提升性能
abstractSyntaxTree?: AST
);
*/
raw loader
在默认的情况下,Webpack 传给 Loader 的原内容都是UTF-8格式编码的字符串。
但有些场景下 Loader 不是处理文本文件,而是处理二进制文件,例如 file-loader,就需要 Webpack 给 Loader 传入二进制格式的数据。 为此,你需要这样编写 Loader
module.exports = function(source) {
// 在 exports.raw === true 时,Webpack 传给 Loader 的 source 是 Buffer 类型的
source instanceof Buffer === true;
// Loader 返回的类型也可以是 Buffer 类型的
// 在 exports.raw !== true 时,Loader 也可以返回 Buffer 类型的结果
return source;
};
// 通过 exports.raw 属性告诉 Webpack 该 Loader 是否需要二进制数据
module.exports.raw = true;
获得option
可以获得给Loader配置的options
const loaderUtils = require('loader-utils');
module.exports = function(source) {
// 获取到用户给当前 Loader 传入的 options
const options = loaderUtils.getOptions(this);
return source;
};
完整API: https://webpack.js.org/api/loaders/
参考
https://juejin.cn/post/6844904038543130637
https://webpack.docschina.org/contribute/writing-a-loader/
https://indexsarrol.cn/2020/12/24/webpack-and-loader/