前言

Loader就像一个翻译员,能将源文件经过转化后输出新的结果,并且这个文件还可以链式地经过多个翻译员翻译。在开发Loader的时候,应该遵循单一职责原则,只需要完成一种转换,开发的过程中我们只关心Loader的输入和输出。webpack是运行在Node.js上的,一个Loader就是一个Node模块。这个模块需要导出一个函数,这个导出的函数的工作就是获得处理前的原内容,对原内容执行处理返回处理后的内容。

一个最简单的Loader

一个最简单的loader源码如下

module.exports = function (source) {
    //source为compiler 传递给 Loader 的一个文件的原内容
    //该函数需要返回处理后的内容,为了简单起见,直接将原内容返回了,相当于该Loader没有做任何转换
    return source
}

开始开发之前

  1. 获取Loaderoptions
    在开发一个Loader的时候,要获取传入的options,需要这样做
    const loaderUtils = require('loader-utils')
    module.exports = function (source) {
     //获取用户传入的options
     const options = loaderUtils.getOptions(this)
     return source
    }
  2. 同步与异步
    Loader有同步和异步之分,上面介绍的Loader是同步的Loader,但在某些场景下转换的步骤只能是异步完成的,例如我们需要通过网络请求才能得出结果,如果采用同步的方式,则网络请求会阻塞整个构建,导致构建非常缓慢。
    如果是异步转换,我们需要这样做
    module.exports = function (source) {
      //告诉webpack此次转换是异步的,Loader会在callback回调结果
      var callback = this.async()
      asyncOperation(source,function(err,result,sourceMaps,ast){
          callback(err,result,sourceMaps,ast)
      })
    }
  3. 加载本地Loader
    假设本地项目的Loader处于./src/loaders/myLoader中,需要如下配置
    module.exports = {
      resolveLoader:{
      //去哪些目录寻找loaders 有先后之分
      modules:['node_modules','./src/loaders']
      }
    }
    或者我们也可以这样配置,直接加载对应的Loader处理对应的文件
    const path = require('path')
    module.exports = {
      module: {
          rules: [{
              test: /\.js$/,
              use: [{
                  loader: path.resolve('loaders/test.js')
              }],
          }]
      },
    }
    

webpack.config.js中加上以上配置后,webpack先会去node_modules目录下寻找,如果找不到就会去./src/loaders下查找

开发我们的Loader

我们现在写一个简单的Loader,这个Loader的作用就是将JavaScript文件内容里面的字符串love转换成 ❤ 。
webpac.config.js的配置如下

//webpack.config.js
const path = require('path')
module.exports = {
    entry: './index.js',
    module: {
        rules: [{
            test: /\.js$/,
            use: [{
                loader: path.resolve('loaders/test.js')
            }],
        }]
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

我们自己写的Loader代码如下

//test.js
module.exports = function (source) {
    const regExp = new RegExp("love", "ig")
    const result = source.replace(regExp, "❤")
    return result
}

入口文件index.js里只有一行代码,即 module.exports="I love you" 执行打包命令npx webpack
可以看到打包后的文件bundle.js

([function (e, t) {
    e.exports = "I ❤ TAPD"
}])

我们的字符'love'已经被转换成了❤