Loader和Plugin比较

694 阅读3分钟

一、为什么webpack默认只有js和json?

因为webpack是基于node。node是遵循CommonJS规范,最早时候的webpack是遵循CommonJS规范

二、Loader

原因:webpack默认只能处理jsjson文件。很多文件无法打包,就需要Loader。所以Loader负责文件转换

Loader用于将模块的原内容按照一定规则转换成新内容

  • 配置方法
module.exports = {
    // ...
    module: {
        rules: [
            { test: /\.txt$/, use: "raw-loader" }
        ]
    }
}
执行顺序

从右到左从下到上

如何实现一个Loader
  • Loader是一个函数,注意不能是箭头函数,因为函数内可以通过this来访问loaderContext,从而可以获取一些相关配置(见下面代码this.query注释)
  • 函数接收source,作为上一个阶段处理返回的代码。处理完成后返回(return)。或者调用this.callback函数
// source: 获取到的代码,用于后续处理
module.exports = function(source) {
    /**
        1.this指向loaderContext
        2.如果loader配置了options对象,那么this.query指向options
    */
    const options = this.qurey;
    
    // 这里对代码进行处理
    var nextSource = formatSource(source)
    
    // 处理完成了,返回代码,交给下一个loader
    // 1.this.callback(null, nextSource) 。多在异步使用
    // 2. return nextSource。多在同步使用
}

三、plugin

  • 配置方法
module.exports = {
    // ...
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ]
}
如何实现一个Plugin

Plugin主要负责功能扩展,在webpack运行的生命周期hooks中会广播许多事件,插件plugin就通过监听这些事件,并拦截webpack的执行,可以在特定阶段执行自己的插件任务

  • 插件是一个对象。或者是一个构造函数。下面以对象为例

  • 对象里必须包含apply函数。参数compiler就是执行webpack获取到的compiler

  • compiler各个生命周期的tap函数(tap函数只是例子,有同步和异步方法,要看具体生命周期),接受两个参数,一个是事件名称,一个是回调函数,回调函数参数叫compilation

  • compilation 对象包含了当前的模块资源编译生成资源变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。

  • 实现一个同步的钩子 同步的不需要有callback

class MyPlugin {
    apply(compiler) {
        // 找到合适的生命周期,注入事件钩子
        compiler.hooks.emit.tap("MyPlugin", compilation => {
            // compilation:当前打包构造流程的上下文
            console.info(compilation);
            
            // do something...
        })
    }
}
  • 一个异步的钩子 下面代码有注释,异步的必须调用callback函数,因为是拦截webpack编译, 只有callback(),才能继续往下执行构建
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
    apply(compiler) {
        compiler.hooks.run.tapAsync(pluginName, (compilation, callback) => {
            setTimeout(() => {
              console.log("webpack 构建过程开始!");
              callback(); // callback 方法为了让构建继续执行下去,必须要调用
            }, 1000);
        });
    }
}

plugin中有异步请求会阻塞后面的plugin吗?

先不要急着回答这个问题,要先去理解,什么是plugin后面的plugin,每一个plugin对应着webpack hooks不同的生命周期,所以理论上plugins数组的顺序并不能代表着执行顺序。

答案:

如果存在异步请求,需要注册异步hooks(tapAsync或者tapPromise),这个时候接收的函数,有一个callback回调函数,只有执行callback()后,编译才会继续往下执行