webpack loader 与plugin的区别

2019-10-10 webpack

# webpack loader 与plugin的区别

前言:经常会被问到loader与plugin的区别,在这里总结一下

# 1. loader

# 1.1 loader插件的写法:

var less = require('less');

// 同步的情况
function LessLoader(source) 
  return source;
}

// 如果插件中存在此方式
function LessLoader(source) {
  var cb = this.async();
  let css;
  less.render(source, function (err, r) { // r.css
    css = r.css;
    cb(err, css);
  });
}

module.exports = LessLoader;

# 1.2 loader的特点:

  1. 指责单一
  2. 使用多个loader需要用数组的形式
  3. loader的执行顺序 默认是从右到左,从下到上。
  4. loader 可以写成对象的形式。

# 1.3 this.async的实现原理

class Demo {
  constructor() {
    this.arr = [];
  }
  push(cb) {
    this.arr.push(cb);
  }
  run() {
    var isHasAsync = false;
    var next =()=> {
      var currCb = this.arr.pop();
      var obj = {
        async: async,
        callback: currCb
      }
      obj.callback();
      if (!isHasAsync && this.arr.length!==0) {
        next();
      }
    }
     next();
    function async () {
      isHasAsync = true;
      return () => {
          next();
      }
    }
  }
}
var demo = new Demo();
demo.push(()=>{
  console.log(1);
})
demo.push(function(params) {
  var cb = this.async();
  console.log(2);
  setTimeout(() => {
    console.log('2结束了');
    cb();
  }, 3000);
})
demo.push(function (params) {
  console.log(3);
   var cb = this.async();
   setTimeout(() => {
     console.log('3结束了');
     cb();
   }, 3000);
})

demo.run();

# 1.4 总结:

根据loader的特点,我们知道loader一定是“同步串行”,上面使用的this.async会阻塞函数执行,只有执行了。cb下一个loader的才可以执行,因此loader本质上是同步的。

# 2. plugin

插件开发基于compiler 钩子,compilation 钩子 (opens new window),webpack在编译代码的时候会触发编译时候的钩子,跟vue的生命周期的钩子非常类似。

# 2.1 插件写法

class plugin2 {
  apply(compiler) {
    compiler.hooks.run.tapAsync('plugin1', (compilation, callback) => {
    console.log('plugin2');
     setTimeout(() => {
        console.log('plugin2 over');
        callback();
     }, 0);
    });
  }
}
module.exports = plugin2;

注意:在写插件的时候一定写,apply函数,webpack在执行的过程中,会去执行apply函数。 **

# 2.1 plugin能取代loader 的功能么?

loader的写法:

 rules:[{
      test: /\.less/,
      use:[
        path.resolve(__dirname, 'loader/css-loader.js'),
        path.resolve(__dirname, 'loader/lessLoader.js')
      ]
    }, {
      test: /\.js/,
      use: [
        // path.resolve(__dirname, 'loader/js1.js'),
        // path.resolve(__dirname, 'loader/js2.js')
      ]
}]

plugin其实也可以实现同样的功能:

由于从本质上研究比较耗时间,这里只是表达,plugin能够做到loader的事情。所以实现的逻辑是在plugin的某一个钩子手动假如loader。

var path = require('path');
var loaders = [{
    loader: '/Users/wanba/code/mycode/webpack4/loader/css-loader.js',
    options: undefined
  },
  {
    loader: '/Users/wanba/code/mycode/webpack4/loader/lessLoader.js',
    options: undefined
  }
]
class plugin1 {
  apply(compiler) {
    compiler.hooks.compilation.tap('plugin1', (compilation, callback) => {    
      compilation.hooks.normalModuleLoader.tap('ss', (conetent, module) => {
        var {resource} =module;
        // console.log(resource)
        if (resource.indexOf('.less') > -1){
            module.loaders = loaders;
        }    
        // console.log(module)
      })

    });
  }
}
module.exports = plugin1;

总结:plugin是完成可以取代loader的,但是千万不要这样子用,违背了webpack的使用方式,应该让他们各尽其职。 **

# 3.区别

  1. loader 用于加载某些资源文件。 因为 webpack 只能理解 JavaScript 和 JSON 文件,对于其他资源例如 css,图片,或者其他的语法集,比如 jsx, coffee,是没有办法加载的。 这就需要对应的loader将资源转化,加载进来。从字面意思也能看出,loader是用于加载的,它作用于一个个文件上。
  2. plugin 用于扩展webpack的功能。 它直接作用于 webpack,扩展了它的功能。当然loader也是变相的扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域。而plugin的功能更加的丰富,而不仅局限于资源的加载。

# 4. plugin 的运行机制是基于事件流。

# 4.1 什么是事件流呢?

举一个现实中的例子:工厂生产衣服,一般都是流水线工程,按顺序执行。一步一步的往下走,不允许插队的情况。如下图:

image.png

webpack 事件流是基于tapablede的,那么,什么是tapable呢? (opens new window)

最后更新: 2019-10-10 7:33:45 ├F10: PM┤