webpack插件

plugin

插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来做相应的钩子

为什么需要一个插件

  • webpack 基础配置无法满足需求
  • 插件几乎能够任意更改 webpack 编译结果
  • webpack 内部也是通过大量内部插件实现的

可以加载插件的常用对象

对象 钩子
Compiler run,compile,compilation,make,emit,done
Compilation buildModule,normalModuleLoader,succeedModule,finishModules,seal,optimize,after-seal
Module Factory beforeResolver,afterResolver,module,parser
Module
Parser program,statement,call,expression
Template hash,bootstrap,localVars,render

创建插件

  • 插件是一个类
  • 类上有一个apply的实例方法
  • apply的参数是compiler
class DonePlugin {
    constructor(options) {
        this.options = options;
    }
    apply(compiler) {

    }
}
module.exports = DonePlugin;

Compiler 和 Compilation

在插件开发中最重要的两个资源就是compilercompilation对象。理解它们的角色是扩展 webpack 引擎重要的第一步。

  • compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。
  • compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。

基本插件架构

  • 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的
  • 这个 apply 方法在安装插件时,会被 webpack compiler 调用一次
  • apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象

使用插件代码

  • [使用插件]https://github.com/webpack/webpack/blob/master/lib/webpack.js#L60-L69)
if (options.plugins && Array.isArray(options.plugins)) {
  for (const plugin of options.plugins) {
    plugin.apply(compiler);
  }
}

Compiler 插件

同步

class DonePlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.done.tap("DonePlugin", (stats) => {
      console.log("Hello ", this.options.name);
    });
  }
}
module.exports = DonePlugin;

异步

class DonePlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.done.tapAsync("DonePlugin", (stats, callback) => {
      console.log("Hello ", this.options.name);
      callback();
    });
  }
}
module.exports = DonePlugin;

使用插件

  • 要安装这个插件,只需要在你的 webpack 配置的 plugin 数组中添加一个实例
const DonePlugin = require("./plugins/DonePlugin");
module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve("build"),
    filename: "bundle.js",
  },
  plugins: [new DonePlugin({ name: "hs" })],
};

compilation 插件

  • 使用 compiler 对象时,你可以绑定提供了编译 compilation 引用的回调函数,然后拿到每次新的 compilation 对象。这些 compilation 对象提供了一些钩子函数,来钩入到构建流程的很多步骤中

webpack-assets-plugin.js

plugins\webpack-assets-plugin.js

//编写个Compilation插件,用来打印本次产出的代码块和文件
class WebpackAssetsPlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    //每当webpack开启一次新的编译 ,就会创建一个新的compilation
    compiler.hooks.compilation.tap('WebpackAssetsPlugin', (compilation) => {
      //每次根据chunk创建一个新的文件后会触发一次chunkAsset
      compilation.hooks.chunkAsset.tap('WebpackAssetsPlugin', (chunk, filename) => {
        console.log(chunk.name || chunk.id, filename);
      });
    });
  }
}
module.exports = WebpackAssetsPlugin;

打包 zip

webpack-archive-plugin.js

plugins\webpack-archive-plugin.js

const jszip = require('jszip');
const { RawSource } = require('webpack-sources');
const { Compilation } = require('webpack');
/**
 * 1.如何获取打出后的文件名和文件内容
 * 2.如何打压缩包 
 * 3.如何向目标目录输出压缩包
 */
class WebpackArchivePlugin {
  constructor(options) {
    this.options = options;
  }
  apply(compiler) {
    compiler.hooks.compilation.tap('WebpackAssetsPlugin', (compilation) => {
      //当确定好文件,当你处理每个资源的时候处执行
      compilation.hooks.processAssets.tapPromise({ name: 'WebpackArchivePlugin' }, (assets) => {
        const zip = new jszip();
        for (const filename in assets) {
          const sourceObj = assets[filename];
          const sourceCode = sourceObj.source();
          zip.file(filename, sourceCode);
        }
        return zip.generateAsync({ type: 'nodebuffer' }).then(zipContent => {
          assets[`archive_${Date.now()}。zip`] = new RawSource(zipContent);
        });
      });
    });
  }
}
module.exports = WebpackArchivePlugin;

webpack.config.js

webpack.config.js

const WebpackArchivePlugin = require('./plugins/webpack-archive-plugin');
  plugins: [
+   new WebpackArchivePlugin({
+     filename:'[timestamp].zip'
+   })
]

自动外链

自动外链

使用外部类库

  • 手动指定 external
  • 手动引入 script

能否检测代码中的 import 自动处理这个步骤?

{
  externals:{
    //key jquery是要require或import 的模块名,值 jQuery是一个全局变量名
  'jquery':'$'
}, 
  module:{}
}

思路

  • 解决 import 自动处理externalscript的问题,需要怎么实现,该从哪方面开始考虑
  • 依赖 当检测到有importlibrary时,将其设置为不打包类似exteral,并在指定模版中加入 script,那么如何检测 import?这里就用Parser
  • external依赖 需要了解 external 是如何实现的,webpack 的 external 是通过插件ExternalsPlugin实现的,ExternalsPlugin 通过tap NormalModuleFactory 在每次创建 Module 的时候判断是否是ExternalModule
  • webpack4 加入了模块类型之后,Parser获取需要指定类型 moduleType,一般使用javascript/auto即可

使用 plugins

plugins: [
  new HtmlWebpackPlugin({
            template:'./src/index.html'
  }),
  new AutoExternalPlugin({
      jquery:{//自动把jquery模块变成一个外部依赖模块
          variable:'jQuery',//不再打包,而是从window.jQuery变量上获取jquery对象
          url:'https://cdn.bootcss.com/jquery/3.1.0/jquery.js'//CDN脚本
      },
      lodash:{//自动把jquery模块变成一个外部依赖模块
          variable:'_',//不再打包,而是从window.jQuery变量上获取jquery对象
          url:'https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js'//CDN脚本
      }
  })
];

AutoExternalPlugin

AsyncSeriesBailHook factorize

let { AsyncSeriesBailHook } = require("tapable");
let factorize = new AsyncSeriesBailHook(['resolveData']);
//最后总得返回一个模块吧 switch case default 
factorize.tapAsync('factory1', (resolveData, callback) => {
  if (resolveData === 'jquery') {
    callback(null, {
      id: resolveData,
      type: '外部模块',
      source: 'window.jQuery'
    });
  } else {
    callback(null);
  }
});
//生成正常模块的工厂函数最后一个工厂函数了,
factorize.tapAsync('factory2', (resolveData, callback) => {
  callback(null, { id: resolveData, type: '正常模块', source: 'webpack打包后的内容' });
});

factorize.callAsync('jquery', (err, module) => {
  console.log(module);
});
factorize.callAsync('lodash', (err, module) => {
  console.log(module);
});

plugins\auto-external-plugin.js

const { ExternalModule } = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');
/**
 * 1.需要向输出html文件中添加CDN脚本引用
 * 2.在打包生产模块的时候,截断正常的打包逻辑,变成外部依赖模块
 */
class AutoExternalPlugin{
    constructor(options){
        this.options = options;
        this.externalModules = Object.keys(this.options);//['jquery'] 进行自动外键的模块
        this.importedModules = new Set();//存放着所有的实际真正使用到的外部依赖模块
    }
    apply(compiler){
        //每种模块会对应一个模块工厂 普通模块对应的就是普通模块工厂
        //https://webpack.docschina.org/api/normalmodulefactory-hooks/
        compiler.hooks.normalModuleFactory.tap('AutoExternalPlugin',(normalModuleFactory)=>{
            //https://webpack.docschina.org/api/parser/#root
            normalModuleFactory.hooks.parser
            .for('javascript/auto')//普通 的JS文件对应的钩子就是'javascript/auto'
            .tap('AutoExternalPlugin',parser=>{
                //在parser遍历语法的过程中,如果遍历到了import节点
                //https://webpack.docschina.org/api/parser/#import
                parser.hooks.import.tap('AutoExternalPlugin',(statement,source)=>{
                    if(this.externalModules.includes(source)){
                        this.importedModules.add(source);
                    }
                });
                //https://webpack.docschina.org/api/parser/#call
                //call=HookMap key方法名 值是这个方法对应的钩子
                parser.hooks.call.for('require').tap('AutoExternalPlugin',(expression)=>{
                    let value = expression.arguments[0].value;
                    if(this.externalModules.includes(value)){
                        this.importedModules.add(value);
                    }
                });
            })
            //2.改造模块的生产过程,如果是外链模块,就直接生产一个外链模块返回
            //https://webpack.docschina.org/api/normalmodulefactory-hooks/
            normalModuleFactory.hooks.factorize.tapAsync('AutoExternalPlugin',(resolveData,callback)=>{
                let {request} = resolveData;//模块名 jquery lodash
                if(this.externalModules.includes(request)){
                    let {variable} = this.options[request];
                    //request=jquery window.jQuery
                    callback(null,new ExternalModule(variable,'window',request));
                }else{
                    callback(null);//如果是正常模块,直接向后执行。走正常的打包模块的流程
                }
            });
        });
        //是往输出的HTML中添加一个新的CDN Script标签
        compiler.hooks.compilation.tap('AutoExternalPlugin',(compilation)=>{
            HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync('AutoExternalPlugin',(htmlData,callback)=>{
                //console.log(JSON.stringify(htmlData,null,2));
                Reflect.ownKeys(this.options).filter(key=>this.importedModules.has(key)).forEach(key=>{
                    //jquery
                    htmlData.assetTags.scripts.unshift({
                        tagName:'script',
                        voidTag:false,
                        attributes:{
                            defer:false,
                            src:this.options[key].url
                        }
                    });
                })
                callback(null,htmlData);
            });
        });

    }
}
module.exports = AutoExternalPlugin;
/**
 * Node {
  type: 'ImportDeclaration',
  start: 0,
  end: 23,
  loc: SourceLocation {
    start: Position { line: 1, column: 0 },
    end: Position { line: 1, column: 23 }
  },
  range: [ 0, 23 ],
  specifiers: [
    Node {
      type: 'ImportDefaultSpecifier',
      start: 7,
      end: 8,
      loc: [SourceLocation],
      range: [Array],
      local: [Node]
    }
  ],
  source: Node {
    type: 'Literal',
    start: 14,
    end: 22,
    loc: SourceLocation { start: [Position], end: [Position] },
    range: [ 14, 22 ],
    value: 'jquery',
    raw: "'jquery'"
  }
}
jquery
 */

AsyncQueue

AsyncQueue

let AsyncQueue = require('webpack/lib/util/AsyncQueue');
let AsyncQueue = require('./AsyncQueue');
function processor(item, callback) {
    setTimeout(() => {
        console.log('process',item);
        callback(null, item);
    }, 3000);
}
const getKey = (item) => {
    return item.key;
}
let queue  = new AsyncQueue({
    name:'createModule',parallelism:3,processor,getKey
});
const start = Date.now();
let item1 = {key:'module1'};
queue.add(item1,(err,result)=>{
    console.log(err,result);
    console.log(Date.now() - start);
});
queue.add(item1,(err,result)=>{
    console.log(err,result);
    console.log(Date.now() - start);
});
queue.add({key:'module2'},(err,result)=>{
    console.log(err,result);
    console.log(Date.now() - start);
});
queue.add({key:'module3'},(err,result)=>{
    console.log(err,result);
    console.log(Date.now() - start);
});
queue.add({key:'module4'},(err,result)=>{
    console.log(err,result);
    console.log(Date.now() - start);
});

use.js

use.js

const QUEUED_STATE = 0;//已经 入队,待执行
const PROCESSING_STATE = 1;//处理中
const DONE_STATE = 2;//处理完成
class ArrayQueue {
    constructor() {
        this._list = [];
    }
    enqueue(item) {
        this._list.push(item);//[1,2,3]
    }
    dequeue() {
        return this._list.shift();//移除并返回数组中的第一个元素
    }
}
class AsyncQueueEntry {
    constructor(item, callback) {
        this.item = item;//任务的描述
        this.state = QUEUED_STATE;//这个条目当前的状态
        this.callback = callback;//任务完成的回调
    }
}
class AsyncQueue {
    constructor({ name, parallelism, processor, getKey }) {
        this._name = name;//队列的名字
        this._parallelism = parallelism;//并发执行的任务数
        this._processor = processor;//针对队列中的每个条目执行什么操作
        this._getKey = getKey;//函数,返回一个key用来唯一标识每个元素
        this._entries = new Map();
        this._queued = new ArrayQueue();//将要执行的任务数组队列 
        this._activeTasks = 0;//当前正在执行的数,默认值1
        this._willEnsureProcessing = false;//是否将要开始处理
    }
    add = (item, callback) => {
        const key = this._getKey(item);//获取这个条目对应的key
        const entry = this._entries.get(key);//获取 这个key对应的老的条目
        if (entry !== undefined) {
            if (entry.state === DONE_STATE) {
                process.nextTick(() => callback(entry.error, entry.result));
            } else if (entry.callbacks === undefined) {
                entry.callbacks = [callback];
            } else {
                entry.callbacks.push(callback);
            }
            return;
        }
        const newEntry = new AsyncQueueEntry(item, callback);//创建一个新的条目
        this._entries.set(key, newEntry);//放到_entries
        this._queued.enqueue(newEntry);//把这个新条目放放队列
        if (this._willEnsureProcessing === false) {
            this._willEnsureProcessing = true;
            setImmediate(this._ensureProcessing);
        }
    }
    _ensureProcessing = () => {
        //如果当前的激活的或者 说正在执行任务数行小于并发数
        while (this._activeTasks < this._parallelism) {
            const entry = this._queued.dequeue();//出队 先入先出
            if (entry === undefined) break;
            this._activeTasks++;//先让正在执行的任务数++
            entry.state = PROCESSING_STATE;//条目的状态设置为执行中
            this._startProcessing(entry);
        }
        this._willEnsureProcessing = false;
    }
    _startProcessing = (entry) => {
        this._processor(entry.item, (e, r) => {
            this._handleResult(entry, e, r);
        });
    }
    _handleResult = (entry, error, result) => {
        const callback = entry.callback;
        const callbacks = entry.callbacks;
        entry.state = DONE_STATE;//把条目的状态设置为已经完成
        entry.callback = undefined;//把callback
        entry.callbacks = undefined;
        entry.result = result;//把结果赋给entry
        entry.error = error;//把错误对象赋给entry
        callback(error, result);
        if (callbacks !== undefined) {
            for (const callback of callbacks) {
                callback(error, result);
            }
        }
        this._activeTasks--;
        if (this._willEnsureProcessing === false) {
            this._willEnsureProcessing = true;
            setImmediate(this._ensureProcessing);
        }
    }
}
module.exports = AsyncQueue;

参考

钩子集合

收集

        Object.keys(this.hooks).forEach(hookName => {
            const hook = this.hooks[hookName];
            if (hook instanceof HookMap) {
                for (let key of hook._map.keys()) {
                    hook.for(key).tap('flow', () => {
                        console.log(`|JavascriptParser|${hookName}|${hook.for(key).constructor.name}|${hook._args}|`);
                    });
                }
            } else {
                hook.tap('flow', () => {
                    console.log(`|JavascriptParser|${hookName}|${hook.constructor.name}|${hook._args}|`);
                });
            }
        });

触发时机

对象 钩子名称 类型 参数 含义
Compiler environment SyncHook 在编译器准备环境时调用,时机就在配置文件中初始化插件之后
Compiler afterEnvironment SyncHook
Compiler entryOption SyncBailHook context,entry
Compiler afterPlugins SyncHook compiler
Compiler afterResolvers SyncHook compiler
Compiler initialize SyncHook
Compiler beforeRun AsyncSeriesHook compiler
Compiler run AsyncSeriesHook compiler
Compiler infrastructureLog SyncBailHook origin,type,args
Compiler readRecords AsyncSeriesHook
Compiler normalModuleFactory SyncHook normalModuleFactory
Compiler contextModuleFactory SyncHook contextModuleFactory
Compiler beforeCompile AsyncSeriesHook params
Compiler compile SyncHook params
Compiler thisCompilation SyncHook compilation,params
Compiler compilation SyncHook compilation,params
Compiler make AsyncParallelHook compilation
Compilation addEntry SyncHook entry,options
NormalModuleFactory beforeResolve AsyncSeriesBailHook resolveData
NormalModuleFactory factorize AsyncSeriesBailHook resolveData
NormalModuleFactory resolve AsyncSeriesBailHook resolveData
NormalModuleFactory afterResolve AsyncSeriesBailHook resolveData
NormalModuleFactory createModule AsyncSeriesBailHook createData,resolveData
NormalModuleFactory module SyncWaterfallHook module,createData,resolveData
Compilation buildModule SyncHook module
Compilation normalModuleLoader SyncHook loaderContext,module
JavascriptParser program SyncBailHook ast,comments
JavascriptParser preStatement SyncBailHook statement
JavascriptParser preStatement SyncBailHook statement
JavascriptParser blockPreStatement SyncBailHook declaration
JavascriptParser preDeclarator SyncBailHook declarator,statement
JavascriptParser blockPreStatement SyncBailHook declaration
JavascriptParser statement SyncBailHook statement
JavascriptParser declarator SyncBailHook declarator,statement
JavascriptParser statement SyncBailHook statement
JavascriptParser finish SyncBailHook ast,comments
Compilation succeedModule SyncHook module
NormalModuleFactory beforeResolve AsyncSeriesBailHook resolveData
NormalModuleFactory factorize AsyncSeriesBailHook resolveData
NormalModuleFactory resolve AsyncSeriesBailHook resolveData
NormalModuleFactory afterResolve AsyncSeriesBailHook resolveData
NormalModuleFactory createModule AsyncSeriesBailHook createData,resolveData
NormalModuleFactory module SyncWaterfallHook module,createData,resolveData
Compilation buildModule SyncHook module
Compilation normalModuleLoader SyncHook loaderContext,module
JavascriptParser program SyncBailHook ast,comments
JavascriptParser preStatement SyncBailHook statement
JavascriptParser blockPreStatement SyncBailHook declaration
JavascriptParser statement SyncBailHook statement
JavascriptParser finish SyncBailHook ast,comments
Compilation succeedModule SyncHook module
Compilation succeedEntry SyncHook entry,options,module
Compilation log SyncBailHook origin,logEntry
Compiler finishMake AsyncSeriesHook compilation
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation finishModules AsyncSeriesHook modules
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation seal SyncHook
Compilation optimizeDependencies SyncBailHook modules
Compilation log SyncBailHook origin,logEntry
Compilation afterOptimizeDependencies SyncHook modules
Compilation log SyncBailHook origin,logEntry
Compilation beforeChunks SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation afterChunks SyncHook chunks
Compilation log SyncBailHook origin,logEntry
Compilation optimize SyncHook
Compilation optimizeModules SyncBailHook modules
Compilation afterOptimizeModules SyncHook modules
Compilation optimizeChunks SyncBailHook chunks,chunkGroups
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation afterOptimizeChunks SyncHook chunks,chunkGroups
Compilation optimizeTree AsyncSeriesHook chunks,modules
Compilation afterOptimizeTree SyncHook chunks,modules
Compilation optimizeChunkModules AsyncSeriesBailHook chunks,modules
Compilation afterOptimizeChunkModules SyncHook chunks,modules
Compilation shouldRecord SyncBailHook
Compilation reviveModules SyncHook modules,records
Compilation beforeModuleIds SyncHook modules
Compilation moduleIds SyncHook modules
Compilation optimizeModuleIds SyncHook modules
Compilation afterOptimizeModuleIds SyncHook modules
Compilation reviveChunks SyncHook chunks,records
Compilation beforeChunkIds SyncHook chunks
Compilation chunkIds SyncHook chunks
Compilation optimizeChunkIds SyncHook chunks
Compilation afterOptimizeChunkIds SyncHook chunks
Compilation log SyncBailHook origin,logEntry
Compilation recordModules SyncHook modules,records
Compilation recordChunks SyncHook chunks,records
Compilation optimizeCodeGeneration SyncHook modules
Compilation log SyncBailHook origin,logEntry
Compilation beforeModuleHash SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation afterModuleHash SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation beforeCodeGeneration SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation afterCodeGeneration SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation beforeRuntimeRequirements SyncHook
Compilation additionalModuleRuntimeRequirements SyncHook module,runtimeRequirements,context
Compilation additionalModuleRuntimeRequirements SyncHook module,runtimeRequirements,context
Compilation log SyncBailHook origin,logEntry
Compilation additionalChunkRuntimeRequirements SyncHook chunk,runtimeRequirements,context
Compilation log SyncBailHook origin,logEntry
Compilation additionalTreeRuntimeRequirements SyncHook chunk,runtimeRequirements,context
Compilation log SyncBailHook origin,logEntry
Compilation afterRuntimeRequirements SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation beforeHash SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation chunkHash SyncHook chunk,chunkHash,ChunkHashContext
Compilation contentHash SyncHook chunk
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation fullHash SyncHook hash
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation afterHash SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation recordHash SyncHook records
Compilation log SyncBailHook origin,logEntry
Compilation beforeModuleAssets SyncHook
Compilation log SyncBailHook origin,logEntry
Compilation shouldGenerateChunkAssets SyncBailHook
Compilation beforeChunkAssets SyncHook
Compilation renderManifest SyncWaterfallHook result,options
Compilation assetPath SyncWaterfallHook path,options,assetInfo
Compilation chunkAsset SyncHook chunk,filename
Compilation log SyncBailHook origin,logEntry
Compilation additionalChunkAssets Object undefined
Compilation additionalAssets Object undefined
Compilation optimizeAssets AsyncSeriesHook assets
Compilation processAssets AsyncSeriesHook assets
Compilation optimizeChunkAssets Object undefined
Compilation afterOptimizeChunkAssets Object undefined
Compilation afterOptimizeAssets SyncHook assets
Compilation afterProcessAssets SyncHook assets
Compilation log SyncBailHook origin,logEntry
Compilation record SyncHook compilation,records
Compilation needAdditionalSeal SyncBailHook
Compilation afterSeal AsyncSeriesHook
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compiler afterCompile AsyncSeriesHook compilation
Compilation log SyncBailHook origin,logEntry
Compiler shouldEmit SyncBailHook compilation
Compiler emit AsyncSeriesHook compilation
Compilation assetPath SyncWaterfallHook path,options,assetInfo
Compiler assetEmitted AsyncSeriesHook file,info
Compiler afterEmit AsyncSeriesHook compilation
Compilation log SyncBailHook origin,logEntry
Compilation needAdditionalPass SyncBailHook
Compiler emitRecords AsyncSeriesHook
Compilation log SyncBailHook origin,logEntry
Compiler done AsyncSeriesHook stats
Compilation log SyncBailHook origin,logEntry
Compilation log SyncBailHook origin,logEntry
Compiler shutdown AsyncSeriesHook
Compilation statsNormalize SyncHook options,context
Compilation statsFactory SyncHook statsFactory,options
Compilation statsPrinter SyncHook statsPrinter,options
Compilation processErrors SyncWaterfallHook errors
Compilation processWarnings SyncWaterfallHook warnings
Compiler afterDone SyncHook stats
Compiler infrastructureLog SyncBailHook origin,type,args

工作流

初始化阶段
事件名 解释 代码位置
读取命令行参数 从命令行中读取用户输入的参数 require(“./convert-argv”)(argv)
实例化 Compiler 1.用上一步得到的参数初始化 Compiler 实例 2.Compiler 负责文件监听和启动编译 3.Compiler 实例中包含了完整的 Webpack 配置,全局只有一个 Compiler 实例。 compiler = webpack(options);
加载插件 1.依次调用插件的 apply 方法,让插件可以监听后续的所有事件节点。 同时给插件传入 compiler 实例的引用,以方便插件通过 compiler 调用 Webpack 提供的 API。 plugin.apply(compiler)
处理入口 读取配置的 Entrys,为每个 Entry 实例化一个对应的 EntryPlugin,为后面该 Entry 的递归解析工作做准备 new EntryOptionPlugin().apply(compiler) new SingleEntryPlugin(context, item, name) compiler.hooks.make.tapAsync
编译阶段
事件名 解释 代码位置
run 启动一次新的编译 this.hooks.run.callAsync
compile 该事件是为了告诉插件一次新的编译将要启动,同时会给插件传入compiler 对象。 compile(callback)
compilation 当 Webpack 以开发模式运行时,每当检测到文件变化,一次新的 Compilation 将被创建。 一个 Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。 Compilation 对象也提供了很多事件回调供插件做扩展。 newCompilation(params)
make 一个新的 Compilation 创建完毕主开始编译 this.hooks.make.callAsync
addEntry 即将从 Entry 开始读取文件 compilation.addEntry this._addModuleChain
moduleFactory 创建模块工厂 const moduleFactory = this.dependencyFactories.get(Dep)
create 创建模块 moduleFactory.create
factory 开始创建模块 factory(result, (err, module) resolver(result this.hooks.resolver.tap(“NormalModuleFactory”
resolveRequestArray 解析loader路径 resolveRequestArray
resolve 解析资源文件路径 resolve
userRequest 得到包括loader在内的资源文件的绝对路径用!拼起来的字符串 userRequest
ruleSet.exec 它可以根据模块路径名,匹配出模块所需的loader this.ruleSet.exec
_run 它可以根据模块路径名,匹配出模块所需的loader _run
loaders 得到所有的loader数组 [results0].concat(loaders, results[1], results[2])
getParser 获取AST解析器 this.getParser(type, settings.parser)
buildModule 开始编译模块 this.buildModule(module buildModule(module, optional, origin, dependencies, thisCallback)
build 开始真正编译入口模块 build(options
doBuild 开始真正编译入口模块 doBuild
执行loader 使用loader进行转换 runLoaders runLoaders
iteratePitchingLoaders 开始递归执行pitch loader iteratePitchingLoaders
loadLoader 加载loader loadLoader
runSyncOrAsync 执行pitchLoader runSyncOrAsync
processResource 开始处理资源 processResource options.readResource iterateNormalLoaders iterateNormalLoaders
createSource 创建源代码对象 this.createSource
parse 使用parser转换抽象语法树 this.parser.parse
parse 解析抽象语法树 parse(source, initialState)
acorn.parse 解析语法树 acorn.parse(code, parserOptions)
ImportDependency 遍历并添加添加依赖 parser.state.module.addDependency(clearDep)
succeedModule 生成语法树后就表示一个模块编译完成 this.hooks.succeedModule.call(module)
processModuleDependencies 递归编译依赖的模块 this.processModuleDependencies(module processModuleDependencies(module, callback) this.addModuleDependencies buildModule
make后 结束make this.hooks.make.callAsync(compilation, err => {}
finish 编译完成 compilation.finish();
结束阶段
事件名 解释 代码位置
seal 封装 compilation.seal seal(callback)
addChunk 生成资源 addChunk(name)
createChunkAssets 创建资源 this.createChunkAssets()
getRenderManifest 获得要渲染的描述文件 getRenderManifest(options)
render 渲染源码 source = fileManifest.render();
afterCompile 编译结束 this.hooks.afterCompile
shouldEmit 所有需要输出的文件已经生成好,询问插件哪些文件需要输出,哪些不需要。 this.hooks.shouldEmit
emit 确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容。 this.emitAssets(compilation this.hooks.emit.callAsync const emitFiles = err this.outputFileSystem.writeFile
this.emitRecords 写入记录 this.emitRecords
done 全部完成 this.hooks.done.callAsync

相关推荐

  1. Webpack浅析

    2024-06-13 09:18:04       62 阅读
  2. webpack

    2024-06-13 09:18:04       27 阅读
  3. webpack学习】webpack之“html-webpack-plugin”

    2024-06-13 09:18:04       57 阅读
  4. webpack5 常用使用

    2024-06-13 09:18:04       49 阅读
  5. webpack配置、使用案例

    2024-06-13 09:18:04       33 阅读
  6. 自定义删除无依赖文件的webpack

    2024-06-13 09:18:04       57 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-13 09:18:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-13 09:18:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-13 09:18:04       87 阅读
  4. Python语言-面向对象

    2024-06-13 09:18:04       96 阅读

热门阅读

  1. DSP28335模块配置模板系列——EQEP模块配置模板

    2024-06-13 09:18:04       38 阅读
  2. git 常用命令

    2024-06-13 09:18:04       29 阅读
  3. Git 备份当前 branch 并回滚到当前版本

    2024-06-13 09:18:04       35 阅读
  4. 网络IO模型:BIO NIO AIO 的区别!!!

    2024-06-13 09:18:04       30 阅读
  5. C# —— 算数运算符

    2024-06-13 09:18:04       29 阅读
  6. 力扣-1984. 学生分数的最小差值

    2024-06-13 09:18:04       31 阅读
  7. Docker面试整理-如何查看和管理Docker容器的日志?

    2024-06-13 09:18:04       30 阅读
  8. 发布你的npm插件包:一步步指南

    2024-06-13 09:18:04       26 阅读
  9. MySQL 保姆级教程(一):了解 SQL

    2024-06-13 09:18:04       34 阅读
  10. HttpSecurity

    2024-06-13 09:18:04       33 阅读
  11. Python3 数据结构

    2024-06-13 09:18:04       27 阅读