webpack-loader与plugin

90 阅读3分钟

Loader

loader基础

  • loader的职责是单一的,只需要完成一种转换, 一个源文件需要多次转换,则需要多个loader串行处理,loader的调用是链式的, 从右往左的,第一个loader拿到源文件转换后,会将转换的结果传递给下一个loader,最后一个loader转换完成会将结果输出给webpack.开发loader只需要关注输入输出结果即可,保持单一原则

loader进阶

同步与异步

  • 使用return source, this.callback(nul,source,sourceMap)的都是同步loader
  • 异步loader: 使用 const callback = this.async(),在处理函数回调返回
// 异步loader示例
module.exports = function(source) {
    // 告诉 Webpack 本次转换是异步的,Loader 会在 callback 中回调结果
    var callback = this.async();
    someAsyncOperation(source, function(err, result, sourceMaps, ast) {
        // 通过 callback 返回异步执行后的结果
        callback(err, result, sourceMaps, ast);
    });
};

获取配置的options

  • wepack提供了loader-utils包,可以获取options
const loaderUtils = require('loader-utils');
module.exports = function(source) {
  // 获取到用户给当前 Loader 传入的 options
  const options = loaderUtils.getOptions(this);
  return source;
};

返回sourceMap

  • 使用this.callback(null, source, sourceMap) 第一个参数是err,没有错误的时候应为null
module.exports = function(source) {
  this.callback(null, source, sourceMaps);
};

处理二进制

类似于file-loader, url-loader是处理文件,需要转换成buffer返回给webpack而不是string

module.exports = function(source) {
    return source;
};
module.exports.raw = true;

module.exports.raw = true;, 告诉webpack使用二进制处理

使用缓存加速

webpack会默认所有loader都使用缓存加速,如果需要关闭可以配置 this.cacheable(fasle)

module.exports = function(source) {
  // 关闭该 Loader 的缓存功能
  this.cacheable(false);
  return source;
};

其他api不学了.

Plugin

  • webpack 就像一条生产线,要经过一些列处理流程后才能将源文件转换成输出结果,这条生产线上的每个流程职责都是单一的,多流程之间存在依赖关系,只有完成当前流程后,才能交给下一个流程去处理,而插件就像是插入到生产线中的一个功能,在特定ed时机对生产线的资源做出处理.
  • webpack这种事件流机制应用了观察者模式,依赖于tapable,webpack运行生命周期中会广播出很多事件,plugin可以监听这些事件也只需要关心这些事件,在合适的时机调用来改变输出结果.

Compiler和Compilation

开发Plugin时最常用的两个对象是Compiler和Compilation,两者是连接Plugin和webpack的桥梁.

  • Compiler: 包含了webpack环境所有的配置信息(可以理解为是 class Compiler的实例),通过compiler.hooks.(webpack生命周期).tapAsync(事件名,事件回调),来绑定事件
  • Compilation: 包含了当前模块资源,编译生成资源等 编写fileListPlugin示例 webpack plugin必须提供apply方法,webpack会遍历所有插件调用apply方法,将compiler实例传给插件
class FileListPlugin {
    constructor({ filename }) {
        this.filename = filename || 'index.md'
    }
    apply(compiler) {
        // 文件准备好了, 进行发射前, 生成文件列表清单
       compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, cb) => {
            let content = `## 文件名  资源大小\r\n`
            let assets = compilation.assets
            Object.entries(assets).forEach(([filename, statObj]) => {
                content += `- ${filename}  ${statObj.size()}\r\n`
            })
            assets[this.filename] = {
                source() {
                    return content
                },
                size() {
                    return content.length
                }
            }
            compilation.assets = assets
            cb()
        })

    }
}
module.exports = FileListPlugin