不要用箭头函数,有自己内部的this
- loader使用
loader 分类: enforce字段 pre在前面的 post 在后面的 normalmodule: {
rules: [
{
test: /\.js$/,
// use: 'loader1',
// 第一种 自己定义的使用绝对路径
use: path.resolve(__dirname, 'loaders', 'loader1')
}
]
}
// 第二种
resolveLoader: {
alias: {
'loader1': path.resolve(__dirname, 'loaders', 'loader1')
}
},
// 第三种
resolveLoader: {
modules: ['node_modules', path.resolve(__dirname, 'loaders')]
},
默认执行顺序 从下到上 从右到左
loader的顺序 pre 》 normal 》 inline 》 post
-! 不会让文件再去通过 pre normal loader去处理
let str = require('-!inline-loader!./a.js') loader!./a.js 行内执行
! 不用normal 去处理
!! 只用行内的loader 处理, 不用 rules中的配置处理
babel-loader
- npm i D @babel/core @babel/preset-env webpack webpack-cli
- 工具类 babelUtils 能拿到 loader中配置的参数, use 中的 options参数
loader 中的this loaderContext
里面有很多关键字
let babel = require('@babel/core')
let loaderUtils = require('loader-utils')
function loader(source) {
// loader 中的this loaderContext
let options = loaderUtils.getOptions(this)
let cb = this.async() // 自带的异步执行
babel.transform(source, {
...options,
sourceMap: true,
filename: this.resourcePath.split('/').pop() // sourcemap 的文件名
}, function(err, result) {
// 异步回调
// 两个参数, 第一个如果添加内容会报错
// 错误 代码 sourcemap
cb(err, result.code, result.map) // 单词写错了 打包没反应
})
}
module.exports = loader
banner-loader (每个文件添加注释)
schema-utils 校验的库
{
test: /\.js$/,
use: { /*测不准*/
loader: 'banner-loader',
options: {
text: '测不准',
filename: path.resolve(__dirname, 'banner.js'), // 模板
}
}
}
let loaderUtils = require('loader-utils')
let validateOption = require('schema-utils')
let fs = require('fs')
function loader(source) {
// 不用缓存
this.cacheable(false)
// 用缓存
this.cacheable && this.cacheable()
let options = loaderUtils.getOptions(this)
let schema = {
type: 'object',
properties: {
text: {
type: 'string'
},
filename: {
type: 'string'
}
}
}
// 这里定义了 使用异步 就只能使用cb 不能 return
let cb = this.async()
// 前两个参数对比,对应的名字和类型是否一致。 第三个参数名字,告诉是在那个文件的
validateOption(schema, options, 'banner-loader')
if(options.filename) {
// watch:true 状态时, 如果文件改变了也会触发重新编译
this.addDependency(options.filename)
fs.readFile(options.filename, 'utf8', function (err, data) {
cb(err, `/*${data}*/${source}`)
})
} else {
cb(null, `/*${options.text}*/${source}`)
}
}
module.exports = loader
file-loader
let loaderUtils = require('loader-utils')
function loader(source) {
this.cacheable && this.cacheable()
// interpolateName获取文件的hash值,并插入值,生成唯一的文件名
let filename = loaderUtils.interpolateName(this, '[hash].[ext]', {content: source})
//发射文件,会在dist目录下面生成一个文件
this.emitFile(filename, source); // 文件 内容
//把原来的路径变成编译后的路径
return `module.exports = '${filename}'`;
}
// 把 source 转为 二进制
loader.raw = true
module.exports = loader
url-loader
let loaderUtils = require('loader-utils')
let mime = require('mime')
function loader(source) {
console.log(source.length)
let options = loaderUtils.getOptions(this)
if (options.limit && options.limit > source.length) {
return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
}else{
return require('./file-loader').call(this, source)
}
return source
}
loader.raw = true
module.exports = loader
style-loader
function loader(source) {
// 在 style-loader 中 导出一个脚本
let str = `
let style = document.createElement('style')
style.innerHTML = ${JSON.stringify(source)} 实现一行
document.head.appendChild(style)
`
return str
}
module.exports = loader
__
其大概的意思是,在style-loader的pitch方法有返回值时,剩余的css-loader的pitch方法、css-loader的normal方法以及style-loader的normal方法都不会执行了。而style-loader的pitch方法里面调用了require(‘!!…/x.css’),这就会把require的css文件当作新的入口文件,重新链式调用剩余的loader函数进行处理。(值得注意的是’!!’是一个标志,表示不会再重复递归调用style-loader,而只会调用css-loader处理了)
style-loader
let loaderUtils = require('loader-utils')
function loader(source) {
console.log(11111111111111)
// 在 style-loader 中 导出一个脚本
let str = `
let style = document.createElement('style')
style.innerHTML = ${JSON.stringify(source)}
document.head.appendChild(style)
`
return str
}
// 在style-loader 上谢了 pitch
// style-loader less-loader css-loader
// 处理完了 style-loader less-loader!css-loader!./index.less
// loader 不执行了
loader.pitch = function (remainingRequest) {// 剩余请求
console.log(2222222222222, remainingRequest)
// 让style-loader 去处理less-loader!css-loader/./index.less
// 取相对路径, !!表示不会重复递归调用style-loader。只会调用less css-loader处理了 ,不会循环引用
let str = `
let style = document.createElement('style');
style.innerHTML = require(${loaderUtils.stringifyRequest(this, '!!' + remainingRequest)});
document.head.appendChild(style);
`
// 如果pitch 有返回值,其他的 pitch方法和loader方法都不会执行了
return str
}
// pitch 的执行顺序是 从左往右, 和 loader相反。
/*
因为
*/
module.exports = loader
css-loader
function loader(source) {
console.log('css-loader11111111111')
let reg = /url\((.+?)\)/g
let pos = 0
let current
let arr = ['let list = []']
while(current = reg.exec(source)) {
let [matchUrl, g] = current // 整体,$1
// exec g情况下reg会有lastIndex 属性,截取url(.1.jpg)前一部分,
let last = reg.lastIndex - matchUrl.length
arr.push(`list.push(${JSON.stringify(source.slice(pos, last))})`)
pos = reg.lastIndex
// 把g替换成require写法
arr.push(`list.push('url(' + require(${g}) + ')')`)
}
arr.push(`list.push(${JSON.stringify(source.slice(pos))})`)
arr.push(`module.exports = list.join('')`)
return arr.join('\r\n')
}
module.exports = loader