编写 Loader 和插件

2019-12-18  本文已影响0人  饼干www

Loader Interface
loader-utils
loader-runner

一个最简单的 loader 代码结构

定义: loader 只是一个导出为函数的 JavaScript 模块

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

使用loader-runner进行loader的调试

定义:允许你在不安装webpack的情况下运行loaders
作用:作为webpack的依赖,在webpack中执行loader
进行loader的开发和调试

import {runLoaders} from 'loader-runner';
runLoaders({
    resource: "/abs/path/to/file.txt?query",
    loaders: [],
    context: {},
    readResource: fs.readFile.bind(fs)
}, function(err, result) {
    // err
    // result
});
属性名 说明
resource 资源的绝对路径(可以增加查询字符串)
loaders loader 的绝对路径(可以增加查询字符串)
context 基础上下文之外的额外 loader 上下文
readResource 读取资源的函数

loader的参数获取

通过loader-utils的getOptions方法获取

const loaderUtils = require("loader-utils");
module.exports = function(content) {
     const { name } = loaderUtils.getOptions(this);
};

loader的异常处理

1、loader 内直接通过 throw 抛出
2、通过\color{red}{this.callback}传递错误

this.callback(
    err: Error | null,
    content: string | Buffer, 
    sourceMap?: SourceMap,
    meta?:any
);

loader的异步处理

通过\color{red}{this.async} 来返回一个异步函数

module.exports = function(input) { 
    const callback = this.async();
    // No callback -> return synchronous results // if (callback) { ... }
    callback(null, input + input); //第一个参数是 Error,第二个参数是处理的结果
};

在 loader 中使用缓存

webpack 中默认开启 loader 缓存
(可以使用 this.cacheable(false) 关掉缓存)
缓存条件: loader 的结果在相同的输入下有确定的输出
(有依赖的 loader 无法使用缓存)

loader 如何进行文件输出

通过\color{red}{this.emitFile}进行文件写入

const loaderUtils = require("loader-utils");
module.exports = function (content) {
    const url = loaderUtils.interpolateName(this, "[hash].[ext]", {
        content,
    });
    this.emitFile(url, content);
    const path = `__webpack_public_path__ + ${JSON.stringify(url)};`;
    return `export default ${path}`;
};

插件的运行环境

只能在 webpack 里面运行

插件的基本结构

基本结构

/* 插件名称 */
class MyPlugin {
    /* apply方法 */
    apply(compiler) {
        /* 插件的hooks */
        compiler.hooks.done.tap(' My Plugin', (
            stats/* stats is passed as argument when done hook is tapped. */
        ) => {
            /* 逻辑 */
            console.log('Hello World!');
        });
    }
}
module.exports = MyPlugin;

插件使用:

plugins: [ new MyPlugin() ]

搭建插件的运行环境

const path = require("path");
const DemoPlugin = require("./plugins/demo-plugin.js");
const PATHS = {
    lib: path.join(__dirname, "app", "shake.js"),
    build: path.join(__dirname, "build")
};
module.exports = {
    entry: {
        lib: PATHS.lib,
    },
    output: {
        path: PATHS.build,
        filename: "[name].js",
    },
    plugins: [new DemoPlugin()]
};

插件中如何获取传递的参数

通过插件的构造函数进行获取

constructor(options) {
    this.options = options;
}
new ZipPlugin({
    filename: 'offline'
})

插件的错误处理

参数校验阶段可以直接 throw 的方式抛出

throw new Error(“ Error Message”);

通过 compilation 对象的 warnings 和 errors 接收

compilation.warnings.push("warning"); 
compilation.errors.push("error");

通过 Compilation 进行文件写入

Compilation 上的 assets 可以用于文件写入
文件写入需要使用 webpack-sources (https://www.npmjs.com/package/webpack-sources)

const {
    RawSource
} = require("webpack-sources");
module.exports = class DemoPlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {
        const {
            name
        } = this.options;
        compiler.plugin("emit", (compilation, cb) => {
            compilation.assets[name] = new RawSource("demo");
            cb();
        })
    }
};
上一篇下一篇

猜你喜欢

热点阅读