• webpack打包原理--webapck-cli及Tapable


    一、webpack-cli

    (1)作用

    • 引入 yargs,对命令行进行定制
    • 分析命令行参数,对各个参数进行转换,组成编译配置项
    • 引用webpack,根据配置项进行编译和构建 

    (2) 命令行工具包 yargs

    • 提供命令和分组参数
    • 动态生成 help 帮助信息

     

     (3)webpack-cli 使用 args 分析

    参数分组 (config/config-args.js),将命令划分为9类:

    • Config options: 配置相关参数(文件名称、运行环境等)
    • Basic options: 基础参数(entry设置、debug模式设置、watch监听设置、devtool设置)
    • Module options: 模块参数,给 loader 设置扩展
    • Output options: 输出参数(输出路径、输出文件名称)
    • Advanced options: 高级用法(记录设置、缓存设置、监听频率、bail等)
    • Resolving options: 解析参数(alias 和 解析的文件后缀设置)
    • Optimizing options: 优化参数
    • Stats options: 统计参数
    • options: 通用参数(帮助命令、版本信息等)

    (4) webpack-cli 执行的结果

    1、webpack-cli对配置文件和命令行参数进行转换最终生成配置选项参数 options

    2、最终会根据配置参数实例化 webpack 对象,然后执行构建流程 

    二、Tapable

    (1)意义

    Tapable 是一个类似于 Node.js 的 EventEmitter 的库, 主要是控制钩子函数的发布与订阅,控制着 webpack 的插件系统。

    (2)Tapable库暴露了很多 Hook(钩子)类,为插件提供挂载的钩子

    (3)Tapable hooks 类型 

     (4)Tapable 的使用 -new Hook 新建钩子

    1、Tapable 暴露出来的都是类方法,new 一个类方法获得我们需要的钩子

    2、class 接受数组参数 options ,非必传。类方法会根据传参,接受同样数量的参数。

    const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);

     (5)Tapable 的使用-钩子的绑定与执行

    Tapable 提供了同步&异步绑定钩子的方法,并且他们都有绑定事件和执行事件对 应的方法。

     (6)Tapable 的使用-hook 基本用法示例

    1. const hook1 = new SyncHook(["arg1", "arg2", "arg3"]);
    2. //绑定事件到webapck事件流
    3. hook1.tap('hook1', (arg1, arg2, arg3) => console.log(arg1, arg2, arg3)) //1,2,3
    4. //执行绑定的事件
    5. hook1.call(1,2,3)

     (7)Tapable 的使用-实际例子演示

    1、定义一个 Car 方法,在内部 hooks 上新建钩子。

    2、分别是同步钩子 accelerate、 brake( accelerate 接受一个参数)、异步钩子 calculateRoutes

    3、使用钩子对应的绑定和执行方法 calculateRoutes

    4、使用 tapPromise 可以返回一个 promise 对象 

    1. const {
    2. SyncHook,
    3. AsyncSeriesHook
    4. } = require('tapable');
    5. class Car {
    6. constructor() {
    7. this.hooks = {
    8. accelerate: new SyncHook(['newspeed']),
    9. brake: new SyncHook(),
    10. calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
    11. }
    12. }
    13. }
    14. const myCar = new Car();
    15. //绑定同步钩子
    16. myCar.hooks.brake.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
    17. //绑定同步钩子 并传参
    18. myCar.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
    19. //绑定一个异步Promise钩子
    20. myCar.hooks.calculateRoutes.tapPromise("calculateRoutes tapPromise", (source, target, routesList, callback) => {
    21. // return a promise
    22. return new Promise((resolve,reject)=>{
    23. setTimeout(()=>{
    24. console.log(`tapPromise to ${source} ${target} ${routesList}`)
    25. resolve();
    26. },1000)
    27. })
    28. });
    29. myCar.hooks.brake.call();
    30. myCar.hooks.accelerate.call(10);
    31. console.time('cost');
    32. //执行异步钩子
    33. myCar.hooks.calculateRoutes.promise('Async', 'hook', 'demo').then(() => {
    34. console.timeEnd('cost');
    35. }, err => {
    36. console.error(err);
    37. console.timeEnd('cost');
    38. });

     (8)Tapable 是如何和 webpack 联系起来的

    1、核心对象 Compiler 继承 Tapable

    2、核心对象 Compilation 继承 Tapable

     3、node_modules/webpack/lib/webpack.js

    1. const webpack = (options, callback) => {
    2. const webpackOptionsValidationErrors = validateSchema(
    3. webpackOptionsSchema,
    4. options
    5. );
    6. if (webpackOptionsValidationErrors.length) {
    7. throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
    8. }
    9. let compiler;
    10. if (Array.isArray(options)) {
    11. compiler = new MultiCompiler(
    12. Array.from(options).map(options => webpack(options))
    13. );
    14. } else if (typeof options === "object") {
    15. //初始化默认配置等
    16. options = new WebpackOptionsDefaulter().process(options);
    17. compiler = new Compiler(options.context);
    18. compiler.options = options;
    19. //内部插件(插件必须有个aplly,传入参数compiler)
    20. new NodeEnvironmentPlugin({
    21. infrastructureLogging: options.infrastructureLogging
    22. }).apply(compiler);
    23. //配置或命令行的插件组装成的options(通过webpack-cli生成)
    24. if (options.plugins && Array.isArray(options.plugins)) {
    25. //遍历将compiler传递给每个插件,插件监听compiler里的hooks事件,
    26. //一旦触发事件,插件就会执行相应方法
    27. for (const plugin of options.plugins) {
    28. if (typeof plugin === "function") {
    29. plugin.call(compiler, compiler);
    30. } else {
    31. plugin.apply(compiler);
    32. }
    33. }
    34. }
    35. compiler.hooks.environment.call();
    36. compiler.hooks.afterEnvironment.call();
    37. //加入内置插件
    38. compiler.options = new WebpackOptionsApply().process(options, compiler);
    39. } else {
    40. throw new Error("Invalid argument: options");
    41. }
    42. if (callback) {
    43. if (typeof callback !== "function") {
    44. throw new Error("Invalid argument: callback");
    45. }
    46. if (
    47. options.watch === true ||
    48. (Array.isArray(options) && options.some(o => o.watch))
    49. ) {
    50. const watchOptions = Array.isArray(options)
    51. ? options.map(o => o.watchOptions || {})
    52. : options.watchOptions || {};
    53. return compiler.watch(watchOptions, callback);
    54. }
    55. compiler.run(callback);
    56. }
    57. return compiler;
    58. };

     (9)模拟 Compiler.js

    1. const {
    2. SyncHook,
    3. AsyncSeriesHook
    4. } = require('tapable');
    5. module.exports = class Compiler {
    6. constructor() {
    7. this.hooks = {
    8. accelerate: new SyncHook(['newspeed']),
    9. brake: new SyncHook(),
    10. calculateRoutes: new AsyncSeriesHook(["source", "target", "routesList"])
    11. }
    12. }
    13. run(){
    14. this.accelerate(10)
    15. this.break()
    16. this.calculateRoutes('Async', 'hook', 'demo')
    17. }
    18. accelerate(speed) {
    19. this.hooks.accelerate.call(speed);
    20. }
    21. break() {
    22. this.hooks.brake.call();
    23. }
    24. calculateRoutes() {
    25. this.hooks.calculateRoutes.promise(...arguments).then(() => {
    26. }, err => {
    27. console.error(err);
    28. });
    29. }
    30. }

    (10)插件 my-plugin.js

    1. const Compiler = require('./Compiler')
    2. class MyPlugin{
    3. constructor() {
    4. }
    5. apply(compiler){
    6. compiler.hooks.brake.tap("WarningLampPlugin", () => console.log('WarningLampPlugin'));
    7. compiler.hooks.accelerate.tap("LoggerPlugin", newSpeed => console.log(`Accelerating to ${newSpeed}`));
    8. compiler.hooks.calculateRoutes.tapPromise("calculateRoutes tapAsync", (source, target, routesList) => {
    9. return new Promise((resolve,reject)=>{
    10. setTimeout(()=>{
    11. console.log(`tapPromise to ${source} ${target} ${routesList}`)
    12. resolve();
    13. },1000)
    14. });
    15. });
    16. }
    17. }

     (11)模拟插件执行

    1. const Compiler = require('./Compiler')
    2. const myPlugin = new MyPlugin();
    3. const options = {
    4. plugins: [myPlugin]
    5. }
    6. const compiler = new Compiler();
    7. for (const plugin of options.plugins) {
    8. if (typeof plugin === "function") {
    9. plugin.call(compiler, compiler);
    10. } else {
    11. plugin.apply(compiler);
    12. }
    13. }
    14. compiler.run();
  • 相关阅读:
    C# 常量
    【JVM】JVM表示浮点数
    java的反射
    17、Flink 之Table API: Table API 支持的操作(1)
    运行手写数字识别例程出现cuDNN Error: cudnnConvolutionForward failed
    [计算机动画]Games103-作业1-刚体动画
    armv8 smpen设置
    【电源专题】为开关稳压器选择正确的工作频率
    虚拟摄像头驱动程序彻底分析
    [附源码]计算机毕业设计springboot校园招聘系统设计
  • 原文地址:https://blog.csdn.net/qq_40542728/article/details/125555477