webpack系列学习五(实现自定义的loader)

240 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

实现自定义的loader

loader是什么?

让webpack支持非js、json模块编译的机制,当遇到非js、json的时候,会通过module去进行配置, 会把相应的模块丢给相应的loader去进行编译,loader编译之后的内容会交给webpack做进一步的处理。

loader 本质上是导出为函数的 JavaScript 模块。loader runner 会调用此函数,然后将上一个 loader 产生的结果或者资源文件传入进去。

起始 loader 只有一个入参:资源文件的内容。compiler 预期得到最后一个 loader 产生的处理结果。这个处理结果应该为 String 或者 Buffer(能够被转换为 string)类型,代表了模块的 JavaScript 源码。另外,还可以传递一个可选的 SourceMap 结果(格式为 JSON 对象)

如何自己编写一个Loader

Loader就是⼀个函数, 声明式函数,不能⽤箭头函数

示例:

  • 创建一个替换源码中字符串的loader
// index.js
console.log('Hello webpack')

// frllk-loader.js
module.exports = function (source) {
  return source.replace('Hello', this.query.name)
}

编写loader需要处理的问题:

 * 官方文档:https://webpack.js.org/contribute/writing-a-loader/
 * 接口文档:https://webpack.docschina.org/api/loaders/
 * loader的结构:
 * 官方约定:
 *    一个loader只能做一件事情
 *    不要用箭头函数
 * 1. loader就是一个函数,但是不可以是箭头函数?
 * 2. loader必须有返回值,string or buffer, 如果没有返回值就会出错 "didn't return a Buffer or StringFinal loader (./myLoaders/frllk-loader.js) didn't return a Buffer or String"
 * 3. loader如何接收配置?
 *    通过loader API, 获取外部参数 
 *    this.query:
 *      如果这个 loader 配置了 options 对象的话,this 就指向这个对象。
 *      如果 loader 中没有 options,而是以 query 字符串作为参数调用时,this.query 就是一个以 ? 开头的字符串。
 * 4. 如何返回多个信息?
 *    this.callback有同步调用和异步调用两种方式
 * 5. loader有异步逻辑如何处理?
 *    需要通过一个接口告诉loader runner是这个loader内部是有异步逻辑或者回调的
 * 6. 多个loader之间是如何配置的呢?
 * 7. 如何处理路径问题
 *    webpack.config.js文件中配置resolveLoader
  • 新建文件frllk-loader-async.js
module.exports = function (source) { // 问题1
  // return source.replace('Hello', this.query.name)
  // 问题2、3
    // const info = source.replace('Hello', this.query.name)
    // this.callback(null, info)
  
    // 问题4
  //   setTimeout(() => {
  //   const info = source.replace('Hello', this.query.name)
  //   return info // Final loader (./myLoaders/frllk-loader.js) didn't return a Buffer or String
  // }, 2000);

  // 解决问题5
  const callback = this.async()
  setTimeout(() => {
    const info = source.replace('Hello', this.query.name)
    callback(null, info)
  }, 2000);
}
  • 新建文件frllk-loader.js
module.exports = function (source) {
  return source.replace('webpack', 'webpack!!!')
}
  • 修改配置文件webpack.config.js
// 7. 如何处理路径问题: 解决了问题7
  resolveLoader: {
    modules: ["./node_modules", "./myLoaders"]
  },
  module: {
    rules: [
      {
        test: /\.js$/, // 如果遇到js后缀的文件,用自己写的loader进行处理
        // 使用第三方的loader默认去node_modules去查找,如果是使用自己写的loader,则是通过path,生成绝对路径
        // use: resolve(__dirname, "./myLoaders/frllk-loader.js")
        // 配置化需求:解决了问题6
        // use: [
        //   {
        //     loader: resolve(__dirname, "./myLoaders/frllk-loader-async.js"), // 需要使⽤node核⼼模块path来处理路径
        //     options: {
        //       name: 'frllk'
        //     }
        //     // console.log('frllk webpack!!!')
        //   },
        //   resolve(__dirname, "./myLoaders/frllk-loader.js") // console.log('Hello webpack!!!')
        // ]
        // 路径处理:解决问题7
        use: [
          {
            loader: "frllk-loader-async",
            options: {
              name: 'frllk'
            }
            // console.log('frllk webpack!!!')
          },
          "frllk-loader" // console.log('Hello webpack!!!')
        ]
      },
    ]
  },