《二十六》自定义 Loader

Loader 用于对模块的源代码进行转换,本质上就是一个导出函数的 JavaScript 模块。loader runner 库会调用导出的函数,并且将上一个 Loader 产生的结果或者资源文件作为参数传进去。

自定义 Loader:

  1. 新建 src/index.js 文件,并编写代码。
    // src/index/.js
    console.log('index')
    
  2. 新建 src/custom-loaders/js-loader.js 文件,并编写自定义 loader 的代码。
    // src/custom-loaders/js-loader.js
    // 导出一个函数,默认接收三个参数:第一个参数是 Webpack 读取到的要加载的模块的内容,会放到 content 中;第二个参数是 sourcemap 映射;第三个参数是 meta 元数据
    module.exports = function (content, sourcemap, meta) {
        // 必须返回处理后的结果
        return content + ';'
    }
    
  3. webpack.config.js 中进行配置。
    // webpack.config.js
    module.exports = {
        // 为了能更明确地看到效果,使用 development 模式,防止 Webpack 进行某些默认的优化
        mode: 'development',
        module: {
            rules: [
                // 使用自定义的 js-loader 来处理 JavaScript 文件
                {
                    test: /\.js$/,
                    // 默认会去 node_modules 下查找要使用的 loader,因为此处使用的是自定义 loader,在 node_modules 中没有,因此要明确告知 Webpack 去哪里找 loader
                    loader: './src/custom-loaders/js-loader',
                    // 也可以结合下面的 resolveLoader,那样的话就不需要明确写出路径了
                    // loader: 'js-loader',
                }
            ]
        },
        // resolveLoader: {
            // 配置去哪里查找 loader
            // modules: ['node_modules', './src/custom-loaders'],
        // }
    }
    
  4. 运行 webpack 命令进行打包,会发现,使用 js-loader 处理 JavaScript 文件成功了。
    在这里插入图片描述

自定义不同功能的 Loader:

同步 Loader:

同步 Loader 在函数执行完之前必须返回内容。

// 同步 Loader,在函数执行完之前必须返回内容
module.exports = function (content) {
  // 可以通过 return 返回
  return content

  // 也可以通过 this.callback() 函数返回。其中 this 是 Loader 的上下文对象,callback() 函数接收四个参数,分别是错误信息、content 内容、sourcemap、meta 元数据
  // this.callback(null, content)
}
异步 Loader:

如果想在 Loader 中执行一些异步操作之后再返回内容的话,使用同步 Loader 就会报错。

// 同步 Loader
module.exports = function (content) {
  // 执行一些异步操作之后再返回内容
  setTimeout(() => {
    return content
  }, 1000)
}

在这里插入图片描述
可以使用异步 Loader。

// 异步 Loader
module.exports = function (content) {
  // 只要调用 this.async(),这个 Loader 就会变成异步 Loader。会返回 callback,允许在之后的某个时刻再调用 callback() 返回内容
  const callback = this.async()
  // 执行一些异步操作之后,再返回内容
  setTimeout(() => {
    callback(null, content+';')
  }, 1000)
}
Row loader

默认情况下,Webpack 会把资源文件转化为 JavaScript 语法格式的字符串后传递给 Loader;可以设置 raw 属性为 true,将会把原始的 Buffer 二进制数据传递给 Loader。

每一个 Loader 都可以用 String 或者 Buffer 的形式传递它的处理结果,Complier 编译器会把它们在 Loader 之间相互转换。

module.exports = function (content) {
  // content 是一个 Buffer 数据
  return content
}
module.exports.raw = true
Pitching Loader

设置 pitch 方法后,在实际从后往前执行 Loader 之前,会先从前往后调用 Loader 上的 pitch 方法。

如果任何 pitch 有返回值,那么 Loader 链被阻断,Webpack 会跳过后面所有的 pitch 和 Loader,直接返回上一个 Loader。

// js-loader1
module.exports = function (content) {
  console.log('js-loader1')
  return content
}

module.exports.pitch = function () {
  console.log('Pitching Loader1')
}
// js-loader2
module.exports = function (content) {
  console.log('js-loader2')
  return content
}

module.exports.pitch = function () {
  console.log('Pitching Loader2')
}
module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.js$/,
        // 使用多个 loader
        use: ['js-loader1','js-loader2'],
      },
    ],
  },
  resolveLoader: {
	  modules: ['node_modules', './src/custom-loaders'],
	}
}

在这里插入图片描述

获取传入自定义 Loader 中的参数:

  1. webpack.config.js 配置文件中给 Loader 传入参数。
    module.exports = {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            use: {
              loader: 'js-loader',
              // 传入 Loader 的参数
              options: {
                name: 'Lee',
                age: 18,
              }
            }
          },
        ],
      },
      resolveLoader: {
    	  modules: ['node_modules', './src/custom-loaders'],
    	}
    }
    
  2. 在自定义 Loader 中获取传入的参数。
module.exports = function (content) {
  // 获取传入 Loader 的参数。其中,this 是 Loader 的上下文对象
  const options = this.getOptions()
  console.log(options)
  
  return content
}
  1. 运行 webpack 命令进行打包,会发现,成功获取到了传入的参数。
    在这里插入图片描述
对传入自定义 Loader 中的参数进行校验:

可以使用 schema 来对参数进行校验。

  1. 安装 schema-utils 库:npm install schema-utils --save-dev
  2. 编写 src/custom-loaders/schema.json 校验文件。
    {
      // options 是一个对象
      "type": "object",
      // 参数
      "properties": {
        "name": {
          // name 是一个字符串,如果错误的话提示需要是一个字符串
          "type": "string",
          "description": "需要是一个字符串"
        },
        "age": {
          "type": "number",
          "description": "需要是一个数字"
        }
      },
      // 除了 properties 中指定的参数外,是否还允许传入其他附加的参数
      "additionalProperties": true,
    }
    
  3. webpack.config.js 配置文件中给 Loader 传入参数。
    module.exports = {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            use: {
              loader: 'js-loader',
              // 传入 Loader 的参数
              options: {
                name: 'Lee',
                age: '18',
              }
            }
          },
        ],
      },
      resolveLoader: {
    	  modules: ['node_modules', './src/custom-loaders'],
    	}
    }
    
  4. 在自定义 Loader 中对传入的参数进行校验。
    const {validate} = require('schema-utils')
    const schema = require('./schema.json')
    
    module.exports = function (content) {
      // 获取传入 Loader 的参数
      const options = this.getOptions()
      // 对参数进行校验
      validate(schema, options)
      
      return content
    }
    
  5. 运行 webpack 命令进行打包,会发现,传入的 age 参数类型报错了。
    在这里插入图片描述

自定义的 babel-loader

babel-loader 本质上是使用 babel 来转换代码的,因此可以自己使用 babel 来实现一个 babel-loader

  1. 新建 src/index.js 文件,并编写代码。
    const name = 'Lee'
    console.log(name)
    
  2. webpack.config.js 配置文件中配置使用自定义的 babel-loader
    module.exports = {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            use: {
              // 使用自定义的 babel-loader
              loader: './src/custom-babel-loader',
              options: {
                presets: [
                  '@babel/preset-env',
                ]
              }
            }
          },
        ],
      },
    }
    
  3. 安装 babel 的核心 @babel/corenpm install @babel/core --save-dev
  4. 新建 src/custom-babel-loader,编写自定义的 babel-loader 代码。
    const babel = require('@babel/core')
    
    module.exports = function (content) {
      // 设置为异步的 Loader
      const callback = this.async()
    
      // 获取传入 Loader 的参数
      const options = this.getOptions()
    
      // 对源代码进行转换
      babel.transform(content, options, (err, result) => {
        if (err) {
          callback(err)
        } else {
          callback(null, result.code)
        }
      })
    }
    
  5. 运行 webpack 命令进行打包,会发现,JavaScript 代码被 babel 转换了。
    在这里插入图片描述

Loader 的执行顺序:

如果对一个模块使用了多个 Loader,默认情况下会从后往前执行 Loader。

Loader 最终其实是由 loader-runner 库来处理的。执行顺序具体的实现逻辑可查看 loader-runner 库的 lib/LoaderRunner.js 文件。

  1. 新建 src/index.js 文件,并编写代码。
    // src/index/.js
    console.log('index')
    
  2. webpack.config.js 中进行配置。
    module.exports = {
      mode: 'development',
      module: {
        rules: [
          {
            test: /\.js$/,
            // 使用多个 loader
            use: ["js-loader1", "js-loader2"],
          },
        ],
      },
      resolveLoader: {
    	  modules: ['node_modules', './src/custom-loaders'],
    	}
    }
    
  3. 新建 src/custom-loaders/js-loader1.js 文件和 src/custom-loaders/js-loader2.js ,并编写自定义 loader 的代码。
    // src/custom-loaders/js-loader1.js
    module.exports = function (content) {
      console.log('js-loader1')
      return content
    }
    
    src/custom-loaders/js-loader2.js
    // Normal Loader
    module.exports = function (content) {
      console.log('js-loader1')
      return content
    }
    
  4. 运行 webpack 命令进行打包,会发现,对一个模块使用了多个 loader,会从后往前执行 Loader。
    在这里插入图片描述

通过配置选项改变 Loader 的执行顺序:

可以通过配置 enforce 选项来改变 Loader 的执行顺序。enforce 选项用来指定 Loader 的种类,属性值有:

  1. normal:普通 Loader。所有的 Loader 默认都是普通 Loader。
  2. inline:内联 Loader。

    import/require 语句中指定要使用的 loader。例如:在导入 CSS 文件时,可以通过 import 'style-loader!css-loader!../css/component.css' 指定使用 style-loader'css-loader 转换 CSS 模块。

  3. pre:前置 Loader。
  4. post:后置 Loader。

这四种 Loader 的执行顺序为 pre -> normal -> inline -> post;如果种类相同,那么执行顺序为从后往前。

测试代码:
  1. webpack.config.js 配置文件中,首先需要将多个 Loader 分别拆分到单独的 rule 对象中,然后配置 enforce 选项。
    module.exports = {
      mode: 'development',
      module: {
        // 将处理 JavaScript 模块的多个 loader 分别拆分到单独的 rule 中
        rules: [
          {
            test: /\.js$/,
            loader: "js-loader1",
            // 配置 enforece
            enforce: 'pre',
          },
          {
            test: /\.js$/,
            loader: "js-loader2",
          },
        ],
      },
      resolveLoader: {
    	  modules: ['node_modules', './src/custom-loaders'],
    	}
    }
    
  2. 运行 webpack 命令进行打包,会发现,js-loader1 被最先执行了。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值