webpack 插件怎么写

摘要

现在项目大部分都使用webpack 进行编译,一方面 webpack 生命力比较旺盛,另一方面 webpack 的生态非常完善。我们90%的场景都能够满足。但有时候也会遇到一些特定的业务场景,比如有时候会遇到需要对编译后的文件进行一些文件注入 等其他特定化的需求。怎么办呢?那就来写一个业务定制化的webpack 插件吧。

官方文档可见: https://webpack.js.org/contribute/writing-a-plugin/#creating-a-plugin

如何编写插件

首先我们了解下一个插件的主要结构

class DtoolPlugin {
    // 构造函数,进行参数校验等功能
    constructor(args) {}
    
    // 定义插件执行的方法体,主要逻辑都在这里完成,webpack 会自动调用此方法
    apply(compiler) {}

}

下面我们来看下 compiler 这个关键对象,compile 提供了 webpack 一系列操作的钩子函数,下面说一说常用的hooks

官方文档可见:https://webpack.js.org/api/compiler-hooks/#watching

下面以真实案例来讲解

	apply(compiler) {
            const metaParams = this.metaParams;
	    const tester = {test: this.test};

	    compiler.hooks.compilation.tap('DtoolPlugin', (compilation) => {
                compilation.hooks.optimizeChunkAssets.tapAsync('DtoolPlugin', (chunks, done) => {
                    wrapChunks(compilation, chunks);
                    done();
                })
            });
            // 注入文件方法
            function wrapChunks(compilation, chunks) {
                chunks.forEach(chunk => {
                    const args = {
                        hash: compilation.hash,
                        chunkhash: chunk.hash
                    };
                    chunk.files.forEach(fileName => {
                        if (ModuleFilenameHelpers.matchObject(tester, fileName)) {
                            const content = 'xxxxx';
                            // 注入内容
                            // assets 对象是编译后的对象,以文件名为key , 值为文件内容 、 文件size 等一系列信息, 通过asset 可以对文件名字 , 文件内容做变更
                            compilation.assets[fileName] = new ConcatSource(
                                compilation.assets[fileName],
                                String(content),
                            );
                    }
                });
            });
        }
    }

hooks 调用的时候有个类型 plugin types 支持 tap , 有些支持异步类型 tapAsync tapPromise

compiler.hooks.run.tapAsync('MyPlugin', (source, target, routesList, callback) => {
  console.log('Asynchronously tapping the run hook.');
  callback();
});

compiler.hooks.run.tapPromise('MyPlugin', (source, target, routesList) => {
  return new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
    console.log('Asynchronously tapping the run hook with a delay.');
  });
});

compiler.hooks.run.tapPromise('MyPlugin', async (source, target, routesList) => {
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log('Asynchronously tapping the run hook with a delay.');
});

posted @ 2019-04-17 10:13  空城夕  阅读(829)  评论(0编辑  收藏  举报