本笔记来自Coderwhy老师Webpack5教程
-
Loader本质上是一个导出为函数的JavaScript模块;
-
loader runner库会调用这个函数,然后将上一个loader产生的结果或者资源文件传入进去;
-
Loader函数会接收三个参数
- content:资源文件的内容
- map:sourcemap相关的数据
- meta:一些元数据
module.exports = function(content,map,meta){
//函数处理代码....
return content
}
使用自己创建的Loader
module.exports = {
module: {
rules: [
{
test: /.js$/,
//直接写loader函数的文件地址
use: ['./loader/myLoader.js']
}
]
}
resolveLoader属性
- 配置resolveLoader属性,可以告诉webpack,我们可以直接去哪个文件夹内寻找loader文件,这样我们就可以像导入node_modules文件夹下的Loader一样,采用十分简洁的形式。
module.exports = {
module: {
rules: [
{
test: /.js$/,
//直接写loader函数的文件名称
use: ['myLoader']
}
]
}
resolveLoader: {
//此处配置从哪里找loader
modules: ['./loaders','node_modules']
}
}
Loader的执行顺序
- NormalLoader的定义
module.exports = function(content,map,meta){
//函数处理代码....
return content
}
- PitchLoader的定义
module.exports.pitch = function(content,map,meta){
//函数处理代码....
return content
}
- 从代码上我们可以看到,Webpack在使用loader时
- 首先执行PitchLoader,并执行loaderContext.loaderIndex++操作
- 然后执行NormalLoader并执行loaderContext.loaderIndex--操作
- 其实这也是为什么loader的执行顺序是相反的
改变Loader的执行顺序
-
拆分成多个Rule对象,通过enforce改变执行顺序
-
enforce一共有四种方式
- 默认所有的loader都是normal
- 在行内设置的loader是inline(在前面将css加载时讲过,import 'loader1!loader2!./test.js')
- 也可以通过enforce设置 pre 和 post;
-
PitchingLoader的执行顺序:post, inline, normal, pre;
-
NormalLoader的执行顺序:pre, normal, inline, post;
同步Loader与异步Loader
同步的loader
-
默认创建的Loader就是同步的Loader;
-
这个Loader必须通过 return 或者 this.callback 来返回结果,交给下一个loader来处理;
- 通常在有错误的情况下,我们会使用 this.callback;
-
this.callback的用法:
- 第一个参数必须是 Error 或者 null
- 第二个参数是一个 string或者Buffer
module.exports.pitch = function(content,map,meta){
//函数处理代码....
this.callback(null,content)
//return content
}
异步的loader
- 在loader内使用this.async(),来把loader变成异步loader
module.exports.pitch = function(content,map,meta){
const callback = this.async();
//函数处理代码....
setTimeout(() => {
callback(null,content)
})
}
loader参数
- 获取传入的参数
- 使用库 loader-utils
//webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.js$/,
//直接写loader函数的文件名称
use: [
{
loader: 'myloader',
options: {
test: '测试获取option'
}
}
]
}
]
}
resolveLoader: {
//此处配置从哪里找loader
modules: ['./loaders','node_modules']
}
}
//loader.js
const { getOptions } = require('loader-utils')
modules.exports = function(content){
const options = getOptions(this)
//处理函数...
return content
}
参数校验
//loader-schema.json
{
"type": "object",
"properties": {
"test": {
"type": "string",
"description": "这是一个测试字段"
},
"additionalProperties": true
}
//loader.js
const { getOptions } = require('loader-utils')
const { validate } = require('schema-utils')
const schema = require(./loader-schema.json)
modules.exports = function(content){
const options = getOptions(this)
//处理函数...
validate(option,schema)
return content
}
Babel-loader案例
//babel-schema.json
{
"type": "object",
"properties": {
"presets": {
"type": "array"
},
"additionalProperties": true
}
const babel = require("@babel/core");
const { getOptions } = require("loader-utils");
const { validate } = require('schema-utils')
const schema = require(./babel-schema.json)
module.exports = function(content) {
// 0.设置为异步的loader
const callback = this.async();
// 1.获取传入的参数
const options = getOptions(this);
validate(option,schema)
// 2.对源代码进行转换
babel.transform(content, options, (err, result) => {
if (err) {
callback(err);
} else {
callback(null, result.code)
}
})
}