webpack(五)--如何编写loader

2,125 阅读2分钟

官方文档

接口文档

Loader就是一个函数, 声明式函数 ,不能⽤箭头函数(因为要上到上下文的this,⽤到this的数据,该函数接受一个参数, 是源码)

拿到源代码,作进⼀步的修饰处理,再返回处理后的源码就可以了

//replaceLoader.js
module.exports = function(source) {
  console.log(source, this, this.query);
return source.replace('kkb','开课吧') };


 
//需要使用node核⼼模块path来处理路径
const path = require('path')
module: {
    rules: [ {
            test: /\.js$/,
            use: path.resolve(__dirname, "./loader/replaceLoader.js")
          }
    ]
},

如何给loader配置参数,loader如何接受参数?

this.query

loader-utils

 //webpack.config.js
module: {
    rules: [
        {
            test: /\.js$/,
            use: [
              {
                loader: path.resolve(__dirname,"./loader/replaceLoader.js"),
                options: {
                    name: "我是自定义loader" 
                }
              }
            ]
        }
    ]
},


//replaceLoader.js
//const loaderUtils = require("loader-utils");//官方推荐处理 loader,query的工具
module.exports = function(source) {
//this.query 通过this.query来接受配置⽂件传递进来的参数
 //return source.replace("哈哈", this.query.name);
 
  const options = loaderUtils.getOptions(this);
  const result = source.replace("哈哈", options.name);
  return source.replace("哈哈", options.name);
}
 

this.callback:

如何返回多个信息,不止是处理好的源码呢,可以使用 this.callback来处理

 
//replaceLoader.js
const loaderUtils = require("loader-utils");//官方推荐处理loader,query 的⼯具
module.exports = function(source) {
  const options = loaderUtils.getOptions(this);
  const result = source.replace("哈哈", options.name);
  this.callback(null, result);
};
//this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);

this.async :如果loader⾥面有异步的事情要怎么处理呢

 
const loaderUtils = require("loader-utils");
module.exports = function(source) {
  const options = loaderUtils.getOptions(this);
  setTimeout(() => {
    const result = source.replace("哈哈", options.name);
    return result;
  }, 1000);
};
//先⽤setTimeout处理下试,发现会报错

<!--我们使⽤this.asycn来处理,他会返回this.callback-->
 
const loaderUtils = require("loader-utils");
module.exports = function(source) {
    const options = loaderUtils.getOptions(this);
    //定义⼀个异步处理,告诉webpack,这个loader⾥有异步事件,在⾥⾯调用下这个异步 
    //callback 就是 this.callback 注意参数的使用
    const callback = this.async();
setTimeout(() => {
    const result = source.replace("哈哈", options.name);
    callback(null, result);
  }, 3000);
};

多个loader的使用 注意顺序

顺序,⾃下而上,⾃右到左

 //replaceLoader.js
module.exports = function(source) {
    return source.replace("哈哈", "word");
};

//replaceLoaderAsync.js
const loaderUtils = require("loader-utils");
module.exports = function(source) {
    const options = loaderUtils.getOptions(this); //定义⼀一个异步处理,告诉webpack,这个loader⾥有异步事件,在⾥面调⽤下这个异步
    const callback = this.async();
    setTimeout(() => {
        const result = source.replace("哈哈", options.name);
        callback(null, result);
      }, 3000);
};



//webpack.config.js
module: {
    rules: [
        {
            test: /\.js$/,
            use: [
              path.resolve(__dirname, "./loader/replaceLoader.js"),
              {
                loader: path.resolve(__dirname,"./loader/replaceLoaderAsync.js"),
                options: { name: "哈哈"}
              }
            ]
            // use: [path.resolve(__dirname,"./loader/replaceLoader.js")]
        }
    ]
},

处理loader的路径问题

 resolveLoader: {
    modules: ["node_modules", "./loader"]
},
module: {
    rules: [ {
            test: /\.js$/,
            use: [
              "replaceLoader",
              {
                loader: "replaceLoaderAsync",
                options: { name: "哈哈"}
            }]
        // use: [path.resolve(__dirname,"./loader/replaceLoader.js")]
    } ]
},
 

参考:loader API

webpack.js.org/api/loaders

懂编译原理真的可以为所欲为