webpack深入开发之–Loader

1. loader 开发场景

当在项目中需要对我们的文件进行处理,但是现有loader无法满足要求的时候, 那么自己开发一个loader,就显得比较重要了。比如,我们在开发组件库中,要想使用markdown组成网站的一个个功能介绍页面。在我们的markdown文件中,需要新创建一个自定义标签,用于国际化,比如必须使用一个<chn>标签, 但这个标签,实际上现有的markdown loader是不支持的, 那么就需要新加一个loader,来进行标签的解析。

2. 本文的讨论深度

本文并不是介绍webpack loader的源码实现, 重点放在我们怎么开发一个可正常运行的loader。 所以下面开始对loader进行一些分析。

3. webpack中loader的分类

在webpack的loader中,其实是分为这几类的:

  • pre 前置, 会优先执行
  • post 后置, 会最后执行
  • normal 普通
  • inline 行内

涉及的是loader的执行顺序。 比如:

module: {
    rules: [
      {
        test: /.less$/,
        use: 'style-loader'
      },
     {
        test: /.less$/,
        use: 'css-loader'
      },
     {
        test: /.less$/,
        use: 'less-loader'
      }
    ]
  },

复制代码

这是我们经常会用的loader, 它的执行顺序其实是自下往上(或者说从右向左)。 所以执行的顺序是:

less-loader -> css-loader -> style-loader

如果我们设置 enforce, 则会明确自定义的执行顺序, 比如:

module: {
    rules: [
      {
        test: /.less$/,
        use: 'less-loader',
        enforce: 'pre'
      },
     {
        test: /.less$/,
        use: 'css-loader'
      },
     {
        test: /.less$/,
        use: 'style-loader',
        enforce: 'post'
      }
    ]
  },

复制代码

像上面这种写法,则手动确定执行顺序:

less-loader -> css-loader -> style-loader

normal 是我们正常在rules中引用loader

inline 是另外一种写法:

 style-loader!css-loader!stylus-loader
复制代码

当然目前大家用inline的场景有限,因为很多时候对loader要传入各种参数。

好, 基础用法和不常用的enforce讲完, 下面,拿normal出来,进行重点讲解

4. Normal Loader 和 Pitching Loader

4.1 Loader的执行总结构

Loader的总体结构是将一个函数 exports出去,供webpack的 runtime(我自己抽象的)使用。

4.2 Normal Loader的开发方式和执行

先把最基础的展示:

// vue-pre-loader.js

const vuePreLoader = (content, map, meta) => {
    console.log('vue PreLoader获取的')
    console.log('vue PreLoader逻辑执行');
    return content;
}
module.exports = vuePreLoader;
复制代码

在webpack中的使用:

const {resolve} = require('path');
// ....
rules: [
    {
        test: /\.vue$/,
        use: [
                {
                    loader: 'vue-loader'
                },
                {
                    loader: resolve(__dirname, './loader/vue-pre-loader.js')
                }
        ]
    },
]

复制代码

在编译.vue文件的时候, 就会先执行我们自己写的loader, 而且会把content打印出来。 这个content是字符串,我们可以对数据进行二次处理。

好了,现在我们再开发一个loader, 用于 vue-loader执行后。

// vue-after-loader.js

const vueAfterLoader = (content, map, meta) => {
    // 比如可以把content中的html注释部分给删除掉
    const regExp = /<!--((\s|\r|\n)*((?!-->).)*\s|\r|\n)*-->/;
    if(regExp.test(content)) {
        content = content.replace(content.match(regExp)[0], '');
    }
    console.log('vueAfterLoader 逻辑执行');
    return content;
}

module.exports = vueAfterLoader;

复制代码

同样,更新webpack中的配置:

const {resolve} = require('path');
// ....
rules: [
    {
        test: /\.vue$/,
        use: [
                {
                    loader: resolve(__dirname, './loader/vue-after-loader.js')
                },
                {
                    loader: 'vue-loader'
                },
                {
                    loader: resolve(__dirname, './loader/vue-pre-loader.js')
                }
        ]
    },
]

复制代码

重新执行的时候,注意以下两点:

  • content 的内容可以进行处理,处理成我们想要的
  • 执行顺序: 先执行vue-pre-loader, 再执行vue-arger-loader

4.3 Pitching Loader是什么

这个概念在我处理的很多项目中并没有用到, 所以,更需要研究一下。 经过研究发现,这个pitching其实是跟熔断相关的。

首先加上这个pitch试试

// vue-pre-loader.js

const vuePreLoader = (content, map, meta) => {
    console.log('vue PreLoader逻辑执行');
    return content;
}
vuePreLoader.pitch = function(remainingRequest, precedingRequest, data) {
    console.log('执行vuePreLoader pitching');
}

module.exports = vuePreLoader;


// vue-after-loader.js

const vueAfterLoader = (content, map, meta) => {
    // 比如可以把content中的html注释部分给删除掉
    const regExp = /<!--((\s|\r|\n)*((?!-->).)*\s|\r|\n)*-->/;
    if(regExp.test(content)) {
        content = content.replace(content.match(regExp)[0], '');
    }
    console.log('vueAfterLoader 逻辑执行');
    return content;
}

vueAfterLoader.pitch = function(remainingRequest, precedingRequest, data) {
    console.log('执行vueAfterLoader pitching');
}

module.exports = vueAfterLoader;

复制代码

再次执行, 我们会发现,执行顺序是这样的:

执行vueAfterLoader pitching -> 执行vuePreLoader pitching -> vue PreLoader逻辑执行 -> vueAfterLoader 逻辑执行

也就是说: pitch的执行是从前向后, pitch执行完毕后,再从后向前执行loader

那么这个pitching 的应用是什么呢?一个重要功能就是前面说的熔断。 当pitch 返回非undefined, 那么就会阻断之前的进程。 比如, 我在vue-pre-loader.js 中做一个return操作

vuePreLoader.pitch = function(remainingRequest, precedingRequest, data) {
    console.log('执行vuePreLoader pitching');
    return '熔断流程';
}

复制代码

那么再看执行结果:

执行vueAfterLoader pitching -> 执行vuePreLoader pitching -> vueAfterLoader 逻辑执行

看到了吧? 它把后面的流程给熔断了, 所以 vue PreLoader逻辑执行这个逻辑就没有执行,就返回到了 vue-after-loader

整体流程是这样的:

image.png

画的不是很好,但是尽量体现他们之间的关系。希望对大家有那么一点点启发。

上面是今天我要表达的全部内容, 在分享给大家的同时,自己对其中的一些原理也有了更深一些的认识, 希望对大家有用。喜欢的话别忘了点个赞喔~~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享