webpack深入了解-loader

84 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

1. loader 的执行顺序

loader是一个到处为函数的模块,入参为上一个loader的结果或者资源文件 webpack Compiler编译会得到最后一个loader产生的结果。

2. loader的分类

loader的类型和loader自己是没什么关系的,用enforce来控制。loader分为4种类型:normal,post,pre,inline; loader 的叠加顺序 = post(后置)+inline(内联)+normal(正常)+pre(前置)

let request = `inline-loader1!inline-loader2!${entryFile}`;

let rules = [
  {
    test: /\.js$/,
    use: ["normal-loader1", "normal-loader2"],
  },
  {
    test: /\.js$/,
    enforce: "post",
    use: ["post-loader1", "post-loader2"],
  },
  {
    test: /\.js$/,
    enforce: "pre",
    use: ["pre-loader1", "pre-loader2"],
  },
];

以下为这几种rules中的loader执行顺序

|-  post-loader1-pitch
        post-loader2-pitch
        inline-loader1-pitch
        inline-loader2-pitch
        normal-loader1-pitch
        normal-loader2-pitch
        pre-loader1-pitch
        pre-loader2-pitch
|- pre-loader2
    pre-loader1
    normal-loader2
    normal-loader1
    inline-loader2
    inline-loader1
    post-loader2
    post-loader1

loader总是从右到左被调用。有些情况下,loader只关心request后面的元数据(metadata),并且忽略前一个loader的结果。在实际(从右到左)执行loader之前,会先 从左到右调用loader上的pitch方法。

3. loader上下文

loader context 表示在loader内使用this可以访问的一些方法或属性。

function runLoaders(options, finalCallback) {
  //resource要处理的资源,或者说要编译的模块路径
  //loaders处理此路径的loaders
  //context指的是loader函数在执行的时候this指针
  //readResource读取文件的方法fs.readFile
  const { resource, loaders = [], context = {}, readResource } = options;
  //loaders现在是一个loader模块的绝对路径,转成一个对象
  const loaderObjects = loaders.map(createLoaderObject);
  const loaderContext = context;//这个对象就是loader执行的时候的this指针
  loaderContext.resource = resource;//加载的模块
  loaderContext.readResource = readResource;//读取文件的方法
  loaderContext.loaders = loaderObjects;//存放loaders对象数组
  loaderContext.loaderIndex = 0;//当前正在处理的loader的索引
  loaderContext.callback = null;//可以手工调用此方法向后执行下一个loader
  loaderContext.async = null;//可以把loader运行从同步变为异步,并返回this.callback
}

loader的执行过程

image.png

4. less-loader

完整的loader比较复杂,这里是一个简单实现。

const less = require('less');
function loader(source) {
  let callback = this.async();
  less.render(source, { filename: this.resource }, (err, output) => {
    callback(err,`module.exports = ${JSON.stringify(output.css)}`);
  });
}
module.exports = loader;

转换前 '@color:red;\n#root{\n color:@color;\n}' 转换后 css:'#root {\n color: red;\n}\n'