不要用箭头函数,有自己内部的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 loaderContextlet options = loaderUtils.getOptions(this)let cb = this.async() // 自带的异步执行babel.transform(source, {...options,sourceMap: true,filename: this.resourcePath.split('/').pop() // sourcemap 的文件名}, function(err, result) {// 异步回调// 两个参数, 第一个如果添加内容会报错// 错误 代码 sourcemapcb(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 不能 returnlet 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 = truemodule.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 = truemodule.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\((.+?)\)/glet pos = 0let currentlet 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.lengtharr.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
