# 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的特点:
- 指责单一
- 使用多个loader需要用数组的形式
- loader的执行顺序 默认是从右到左,从下到上。
- 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.区别
- loader 用于加载某些资源文件。 因为 webpack 只能理解 JavaScript 和 JSON 文件,对于其他资源例如 css,图片,或者其他的语法集,比如 jsx, coffee,是没有办法加载的。 这就需要对应的loader将资源转化,加载进来。从字面意思也能看出,loader是用于加载的,它作用于一个个文件上。
- plugin 用于扩展webpack的功能。 它直接作用于 webpack,扩展了它的功能。当然loader也是变相的扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域。而plugin的功能更加的丰富,而不仅局限于资源的加载。
# 4. plugin 的运行机制是基于事件流。
# 4.1 什么是事件流呢?
举一个现实中的例子:工厂生产衣服,一般都是流水线工程,按顺序执行。一步一步的往下走,不允许插队的情况。如下图:
webpack 事件流是基于tapablede的,那么,什么是tapable呢? (opens new window)