不要用箭头函数,有自己内部的this


  • loader使用
    1. module: {
    2. rules: [
    3. {
    4. test: /\.js$/,
    5. // use: 'loader1',
    6. // 第一种 自己定义的使用绝对路径
    7. use: path.resolve(__dirname, 'loaders', 'loader1')
    8. }
    9. ]
    10. }
    11. // 第二种
    12. resolveLoader: {
    13. alias: {
    14. 'loader1': path.resolve(__dirname, 'loaders', 'loader1')
    15. }
    16. },
    17. // 第三种
    18. resolveLoader: {
    19. modules: ['node_modules', path.resolve(__dirname, 'loaders')]
    20. },
    loader 分类: enforce字段 pre在前面的 post 在后面的 normal
    默认执行顺序 从下到上 从右到左
    loader的顺序 pre 》 normal 》 inline 》 post
  1. -! 不会让文件再去通过 pre normal loader去处理
  2. let str = require('-!inline-loader!./a.js') loader!./a.js 行内执行
  3. ! 不用normal 去处理
  4. !! 只用行内的loader 处理, 不用 rules中的配置处理

babel-loader

  • npm i D @babel/core @babel/preset-env webpack webpack-cli
  • 工具类 babelUtils 能拿到 loader中配置的参数, use 中的 options参数

loader 中的this loaderContext
里面有很多关键字

  1. let babel = require('@babel/core')
  2. let loaderUtils = require('loader-utils')
  3. function loader(source) {
  4. // loader 中的this loaderContext
  5. let options = loaderUtils.getOptions(this)
  6. let cb = this.async() // 自带的异步执行
  7. babel.transform(source, {
  8. ...options,
  9. sourceMap: true,
  10. filename: this.resourcePath.split('/').pop() // sourcemap 的文件名
  11. }, function(err, result) {
  12. // 异步回调
  13. // 两个参数, 第一个如果添加内容会报错
  14. // 错误 代码 sourcemap
  15. cb(err, result.code, result.map) // 单词写错了 打包没反应
  16. })
  17. }
  18. module.exports = loader

banner-loader (每个文件添加注释)

schema-utils 校验的库

  1. {
  2. test: /\.js$/,
  3. use: { /*测不准*/
  4. loader: 'banner-loader',
  5. options: {
  6. text: '测不准',
  7. filename: path.resolve(__dirname, 'banner.js'), // 模板
  8. }
  9. }
  10. }
  11. let loaderUtils = require('loader-utils')
  12. let validateOption = require('schema-utils')
  13. let fs = require('fs')
  14. function loader(source) {
  15. // 不用缓存
  16. this.cacheable(false)
  17. // 用缓存
  18. this.cacheable && this.cacheable()
  19. let options = loaderUtils.getOptions(this)
  20. let schema = {
  21. type: 'object',
  22. properties: {
  23. text: {
  24. type: 'string'
  25. },
  26. filename: {
  27. type: 'string'
  28. }
  29. }
  30. }
  31. // 这里定义了 使用异步 就只能使用cb 不能 return
  32. let cb = this.async()
  33. // 前两个参数对比,对应的名字和类型是否一致。 第三个参数名字,告诉是在那个文件的
  34. validateOption(schema, options, 'banner-loader')
  35. if(options.filename) {
  36. // watch:true 状态时, 如果文件改变了也会触发重新编译
  37. this.addDependency(options.filename)
  38. fs.readFile(options.filename, 'utf8', function (err, data) {
  39. cb(err, `/*${data}*/${source}`)
  40. })
  41. } else {
  42. cb(null, `/*${options.text}*/${source}`)
  43. }
  44. }
  45. module.exports = loader

file-loader

  1. let loaderUtils = require('loader-utils')
  2. function loader(source) {
  3. this.cacheable && this.cacheable()
  4. // interpolateName获取文件的hash值,并插入值,生成唯一的文件名
  5. let filename = loaderUtils.interpolateName(this, '[hash].[ext]', {content: source})
  6. //发射文件,会在dist目录下面生成一个文件
  7. this.emitFile(filename, source); // 文件 内容
  8. //把原来的路径变成编译后的路径
  9. return `module.exports = '${filename}'`;
  10. }
  11. // 把 source 转为 二进制
  12. loader.raw = true
  13. module.exports = loader

url-loader

  1. let loaderUtils = require('loader-utils')
  2. let mime = require('mime')
  3. function loader(source) {
  4. console.log(source.length)
  5. let options = loaderUtils.getOptions(this)
  6. if (options.limit && options.limit > source.length) {
  7. return `module.exports = "data:${mime.getType(this.resourcePath)};base64,${source.toString('base64')}"`
  8. }else{
  9. return require('./file-loader').call(this, source)
  10. }
  11. return source
  12. }
  13. loader.raw = true
  14. module.exports = loader

style-loader

  1. function loader(source) {
  2. // 在 style-loader 中 导出一个脚本
  3. let str = `
  4. let style = document.createElement('style')
  5. style.innerHTML = ${JSON.stringify(source)} 实现一行
  6. document.head.appendChild(style)
  7. `
  8. return str
  9. }
  10. 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

  1. let loaderUtils = require('loader-utils')
  2. function loader(source) {
  3. console.log(11111111111111)
  4. // 在 style-loader 中 导出一个脚本
  5. let str = `
  6. let style = document.createElement('style')
  7. style.innerHTML = ${JSON.stringify(source)}
  8. document.head.appendChild(style)
  9. `
  10. return str
  11. }
  12. // 在style-loader 上谢了 pitch
  13. // style-loader less-loader css-loader
  14. // 处理完了 style-loader less-loader!css-loader!./index.less
  15. // loader 不执行了
  16. loader.pitch = function (remainingRequest) {// 剩余请求
  17. console.log(2222222222222, remainingRequest)
  18. // 让style-loader 去处理less-loader!css-loader/./index.less
  19. // 取相对路径, !!表示不会重复递归调用style-loader。只会调用less css-loader处理了 ,不会循环引用
  20. let str = `
  21. let style = document.createElement('style');
  22. style.innerHTML = require(${loaderUtils.stringifyRequest(this, '!!' + remainingRequest)});
  23. document.head.appendChild(style);
  24. `
  25. // 如果pitch 有返回值,其他的 pitch方法和loader方法都不会执行了
  26. return str
  27. }
  28. // pitch 的执行顺序是 从左往右, 和 loader相反。
  29. /*
  30. 因为
  31. */
  32. module.exports = loader

css-loader

  1. function loader(source) {
  2. console.log('css-loader11111111111')
  3. let reg = /url\((.+?)\)/g
  4. let pos = 0
  5. let current
  6. let arr = ['let list = []']
  7. while(current = reg.exec(source)) {
  8. let [matchUrl, g] = current // 整体,$1
  9. // exec g情况下reg会有lastIndex 属性,截取url(.1.jpg)前一部分,
  10. let last = reg.lastIndex - matchUrl.length
  11. arr.push(`list.push(${JSON.stringify(source.slice(pos, last))})`)
  12. pos = reg.lastIndex
  13. // 把g替换成require写法
  14. arr.push(`list.push('url(' + require(${g}) + ')')`)
  15. }
  16. arr.push(`list.push(${JSON.stringify(source.slice(pos))})`)
  17. arr.push(`module.exports = list.join('')`)
  18. return arr.join('\r\n')
  19. }
  20. module.exports = loader