关于前端:工程化之webpack打包过程

10次阅读

共计 12326 个字符,预计需要花费 31 分钟才能阅读完成。

人之所以不高兴,次要是缘于过来和将来:为过来耿耿于怀 为将来惴惴不安

大家好,我是 柒八九

在后期,咱们开了一个对于 前端工程化 的系列文章。曾经从

  • 概念介绍

    • 何为脚手架
    • SourceMap 的惯例概念
    • 在 Webpack 中针对 SourceMap 的配置
  • 构建工具

    • 构建解决的问题
    • 包管理工具
    • 模块化常见形式

等角度进行了一些惯例概念的介绍和梳理。而明天,咱们抉择了一个在前端范畴内,占很大比重的构建工具 –Webpack

近一年的 npm 下载量

github 对应的 stars

能够看到,无论是从 npm 下载量github 的 star 的数量 ,Webpack 都遥遥领先于其余工具(grunt/gulp/rollup/swc)

Webpack 是一个十分弱小的构建工具,它能够被认为是当今许多技术中的一个根本组件,前端开发人员应用它来构建他们的应用程序。

好了,话不多说,持续赶路。

你能所学到的知识点

  1. Webpack常见概念(entry/output/loader)
  2. entry 对象 {a:'./a.js'} 如何构建一个模块树
`entry`=>`EntryPlugin`=>`EntryDependency`=>`NormalModuleFactory`=> 模块树
  1. ModuleGraph 如何 跟踪已建模块 间接的关系
  2. ModuleGraph是如何被构建的
  3. Module/Chunk/ ChunkGroup /EntryPoint具体是啥并它们间接的关系
  4. ChunkGraph是如何被构建的

文章概要

  1. Webpack基本概念简讲
  2. 打包流程总览
  3. entry对象
  4. 深刻了解 ModuleGraph
  5. 构建 ModuleGraph
  6. Module/Chunk/ ChunkGroup /EntryPoint 是个啥
  7. 提交 chunk 资源

0. Webpack基本概念简讲

实质上,webpack 是一个古代 JavaScript 应用程序的 动态<span style=”font-weight:800;color:#FFA500;font-size:18px”>{模块打包器 | module bundler}</span>

webpack 解决应用程序时,它会 递归 地构建一个 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{依赖关系图 | dependency graph}</span>,其中蕴含应用程序须要的每个模块,而后将所有这些模块打包成一个或多个 bundle

运行形式

在我的项目中有两种运行 Webpack 的形式:

  1. 基于命令行 的形式

    webpack --config webpack.config.js

  2. 基于代码 的形式

    var webpack = require('webpack') 
    var config = require('./webpack.config')
    webpack(config, (err, stats) => {})

    重要概念

    针对 Webpack 有几个重要的概念须要通晓。

关键字 作用
Entry Webpack 的入口文件
指的是应该从哪个模块作为入口,来构建 外部依赖图
Output 通知 Webpack 在哪输入它所创立的 bundle 文件
以及输入的 bundle 文件该如何 命名 输入到哪个门路下 等规定
Loader 模块代码转化器
使得 Webpack 有能力去解决 除了 JS、JSON 以外的其余类型 的文件
Plugin Plugin 提供执行更广的工作的性能
包含: 打包优化 资源管理 注入环境变量
Mode 依据 不同运行环境 执行不同优化参数时的必要参数
Browser Compatibility 反对所有 ES5 规范的浏览器(IE8 以上)

1. 打包流程总览

在此篇文章中,咱们只针对 NormalModules 进行解说。当然,还有其余类型的模块 如 ExternalModuleConcatenatedModule(当应用 require.context() 时)这些都不在咱们探讨范畴内。

先来一个 总体流程,润润嗓子。(莫慌,要害的点,前面都会做解释)


2. entry对象

所有 都从 entry 对象开始

咱们用一个简略例子来阐明它的作用,在这个例子中,entry对象只是一个 键值对的汇合

// webpack.config.js
entry: {
    a: './a.js',
    b: './b.js',
    /* ... */
}

webpack 中的 模块与文件是一一对应 的。

因而,在下面的代码中,a.js会产生一个 新的模块 ,而b.js 也会产生。

模块是文件的升级版
模块,一旦创立和构建,除了 源代码,还蕴含很多有意义的信息,如:

  • 应用的 加载器
  • 它的 依赖关系
  • 它的 进口(如果有的话)
  • 它的 哈希值

同时 entry 对象中的每一项都能够被认为是模块树中的根模块 模块树 是因为根模块可能须要一些其余的模块,这些模块可能须要其余的模块,等等。所有这些模块树都被贮存在 ModuleGraph

咱们须要提到的下一件事是,webpack 是建设在很多插件之上的 。人们有很多办法能够退出 自定义逻辑 webpack 的可扩展性是通过hook 实现的 。例如,你能够在 ModuleGraph 建设后,当一个新的 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{资源 |asset}</span> 被生成时,在模块行将被 建设前 (运行加载器和解析源代码),增加自定义逻辑。大多数时候,hook依据它们的目标分组的 ,对于任何定义好的目标,都有一个 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{插件 | plugin}</span>。例如,有一个插件负责解决import() 函数(负责解析正文和参数)– 它被称为 ImportParserPlugin,它所做的就是在 AST 解析过程中遇到 import() 调用时,增加一个hook

有几个插件负责解决 entry 对象。有一个 EntryOptionPlugin,它实际上是 接管 entry 对象,并为对象中的每个我的项目创立一个 EntryPlugin对象 entry 对象的 每个我的项目都会产生一棵模块树(所有这些树都是互相拆散的)。基本上,EntryPlugin 开始创立一个模块树,每个模块都会在 同一个中央ModuleGraph)增加信息。

EntryPlugin 也是创立 EntryDependency 的中央。

基于上图,让咱们简略地实现自定义的EntryOptionsPlugin

class CustomEntryOptionPlugin {// 这是创立插件的规范办法(实现 apply 办法)
  // 就是一个简略的函数
  apply(compiler) {
    compiler.hooks.entryOption.tap('CustomEntryOptionPlugin', entryObject => {

      // 对于 `entryObject` 中的每个项,咱们为其创立一个模块树。// 记住,每个模块树都是独立的
      
               // `entryObject` 能够是这样的。`{a: './a.js'}`
      for (const name in entryObject) {const fileName = entryObject[name];
        // 当打包过程开始时,筹备为这个 entryObject 创立一个模块树。new EntryOption({name, fileName}).apply(compiler);
      };
    });
  }
};

代码解析:

  • hook 为咱们提供了 染指打包过程 的可能性。
  • entryOptionhook 的帮忙下,咱们增加了一个逻辑。
  • 此时基本上意味着打包过程的开始。
  • entryObject参数将保留来自 配置文件 entry对象。
  • 配置文件中的 entry 对象,将用它来设置创立模块树。

EntryOption插件的定义

// `EntryOption` 类将解决模块树的创立。class EntryOption {constructor (options) {this.options = options;};

  // 这依然是一个插件,所以咱们要恪守规范(实现 apply)
  apply(compiler) {compiler.hooks.start('EntryOption', ({ createModuleTree}) => {
      // 基于这个插件的配置,创立新的模块树。createModuleTree(new EntryDependency(this.options));
    });
  };
};

代码解析:

  • start钩子标记着 打包过程的开始 。它将在调用hooks.entryOption 之后被调用。
  • options蕴含 entry 名称(实质上就是块的名称)和文件名。
  • EntryDependency封装了这些选项,同时也提供了创立模块的办法。
  • 在调用 createModuleTree 后,文件的源代码将被找到
  • 而后,一个 模块实例 将被创立,而后 webpack 将失去它的AST,并且将在打包过程中进一步应用

下面代码中,咱们提到了EntryDependency,咱们来一步理解一下。

当波及到 创立新模块 时,这所有都归结为一个 形象过程 。简略地说,一个 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{依赖关系 |dependency}</span> 只是一个 理论模块实例的初步入口 。例如,在 webpack 的观点中,甚至entry 对象的项也是 依赖关系 它们表明了创立模块实例的最低限度 :它的门路(例如./a.js, ./b.js)。 如果没有依赖关系,模块的创立就无奈开始 ,因为依赖关系除其余重要信息外,还蕴含 模块的申请 ,即能够找到 模块源代码的文件的门路(例如 ”./a.js”)。

依赖关系还表明 如何构建该模块 ,它通过 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{模块工厂 |ModuleFactory}</span> 来实现。 模块工厂晓得如何从一个原始状态(例如源代码是一个简略的字符串)开始,而后达到具体的实体文件,而后被 webpack 利用 EntryDependency 实际上是 ModuleDependency 的一种类型,意味着它必定会持有模块的申请,它所指向的模块工厂是 NormalModuleFactory。那么,NormalModuleFactory 就晓得要做什么,以便 从一个门路中创立对 webpack 有意义的货色

另一种思考形式是,模块起初只是一个简略的门路 (要么在entry 对象中,要么是 import 语句的一部分),而后它变成了一个依赖关系,最初变成了一个模块

因而,EntryDependency 在一开始创立模块树的根模块时就被应用
对于其余的模块,还有 其余类型 的依赖关系。例如,如果你应用 import 语句,比方从 ./a.js 导入 defaultFn,那么就会有一个 HarmonyImportSideEffectDependency./a.js持有模块的申请,也映射到 NormalModuleFactory。因而,将有一个新的模块用于 ’a.js’ 文件

疾速回顾一下咱们在本节中所学到的内容:

  • 对于 entry 对象中的每一项,都会有一个 EntryPlugin 实例,其中创立了一个 EntryDependency
  • 这个 EntryDependency 保留了模块的申请 (即文件的门路),并且通过映射到一个模块工厂,即 NormalModuleFactory 提供了一种使该申请有用的办法
  • <span style=”font-weight:800;color:#FFA500;font-size:18px”>{模块工厂 |ModuleFactory}</span> 晓得如何 从一个文件门路中创立对 webpack 有用的实体
  • <span style=”font-weight:800;color:#FFA500;font-size:18px”>{依赖关系 |dependency}</span> 对创立一个模块至关重要,因为它领有重要的信息,比方模块的申请和如何解决该申请。依赖关系有几种类型,并不是所有的依赖关系都对创立一个新模块有用。
  • 从每个 EntryPlugin 实例,在新创建的 EntryDependency 的帮忙下,将 创立一个模块树。模块树是建设在模块和它们的依赖关系之上的,这些模块也能够有依赖关系。

3. 深刻了解 ModuleGraph

ModuleGraph 是一种 跟踪已建模块的办法

它在很大水平上依赖于 <span style=”font-weight:800;color:#FFA500;font-size:18px”>{依赖关系 |dependency}</span>,因为它们提供了连贯两个不同模块的办法。

比如说。

// a.js
import defaultBFn from '.b.js/';

// b.js
export default function () { console.log('我是大帅 B!'); }

这里咱们 有两个文件,所以有两个模块 文件 a 须要 文件 b 的一些货色,所以在 a 中存在一个依赖关系,这个 依赖关系是通过导入语句建设的 。就 ModuleGraph 而言, 依赖关系定义了一种连贯两个模块的形式。下面的片段能够被可视化为:

ModuleGraph 的节点被称为 ModuleGraphModule,它只是一个 装璜过的 NormalModule 实例 ModuleGraph 在一个map 对象的帮忙下跟踪这些装璜过的模块,它有这样的签名:Map<Module, ModuleGraphModule>

例如,如果只有 NormalModule 实例,那么你对它们就没有什么可做的,它们不晓得如何互相交换 ModuleGraph 赋予这些 原始模块 能通过上述 map 的帮忙将它们 相互连接起来的能力 map为每个 NormalModule 调配了一个 ModuleGraphModule。咱们将把 属于 ModuleGraph 的模块也称为模块 ,因为NormalModuleModuleGraphModule区别在于只包含一些额定的无关紧要属性。

对于一个属于 ModuleGraph 的节点来说,有几件事件是明确的:传入的连贯 传出的连贯 。连贯是 ModuleGraph 另一个小实体 ,它领有有意义的信息,如: 起源模块 指标模块 和连贯上述两个模块的 依赖关系。具体来说,基于上图,一个新的连贯曾经被创立。

// 这是以下面的图和代码为根底的造成的连贯关系
Connection: {
    originModule: A, // 起源模块
    destinationModule: B, // 指标模块
    dependency: ImportDependency // 依赖关系
}

而上述 连贯对象 将被增加到 A.outgoingConnections 集和和 B.incomingConnections 集和中。

所有从 entry 中创立的模块树都将向同一个繁多的中央 ,即向ModuleGraph 输入有意义的信息。这是因为 所有这些模块树最终都将与空模块(ModuleGraph 的根模块)相连 。与空模块的连贯是通过 EntryDependency 和从entry 文件中创立的模块建设的。

空模块与每个模块树的根模块有一个连贯 ,该模块由entry 对象中的一个我的项目生成。图中的每条边都代表 2 个模块之间的连贯,每个连贯都有对于 源节点 指标节点 依赖关系 的信息。


4. 构建 ModuleGraph

ModuleGraph 从一个 空模块开始 ,其 直系子孙是模块树的根模块 ,这些模块是由entry 对象项构建的

首批创立的模块

咱们从一个简略的 entry 对象开始。

entry: {a: './a.js',}

正如咱们上文说到,在某些时候,咱们最终会失去一个 EntryDependency,其申请是 ./a.js。这个 EntryDependency 提供了一种 从该申请创立有意义的货色的办法,因为它映射到一个模块工厂,即 NormalModuleFactory

这个过程的下一步是 NormalModuleFactory 发挥作用的中央。NormalModuleFactory,如果它胜利地实现了它的工作,将 创立一个 NormalModule

NormalModule 只是一个文件源代码的反序列化版本,它只不过是一个原始字符串。
一个原始的字符串不会带来太多的价值,所以 webpack 不能用它做什么。一个 NormalModule 会将源代码存储为一个字符串 ,然而,与此同时,它也会蕴含其余 有意义的信息和性能,比方:

  • 利用于它的 加载器
  • 构建模块的逻辑
  • 生成运行时代码的逻辑
  • 它的 哈希值 等等

换句话说,webpack 的角度来看,NormalModule 是一个简略原始文件的有用版本

为了让 NormalModuleFactory 输入一个 NormalModule,它必须要通过一些步骤。在模块被创立后,还有一些事件要做,比方构建模块和解决其依赖关系(如果有的话)。

NormalModuleFactory 通过调用 create() 办法开始。而后,解析过程开始。在这里,申请(文件的门路)被解析,以及该类型文件的加载器。

留神 只有加载器的文件门路将被确定,加载器在这一步还没有被调用


module 的构建过程

在所有必要的文件门路被解决后,NormalModule 被创立 。然而,在这步,模块的价值不大。 很多相干的信息将在模块建设后呈现NormalModule 的构建过程还包含一些其余步骤。

  • 首先,loader将在原始源代码上被调用;如果有 多个加载器 ,那么 一个加载器的输入可能是另一个加载器的输出(配置文件中提供加载器的程序很重要)。
  • 其次,通过 所有加载器运行后 失去的 字符串 将被acornJavaScript 解析器)解析,失去给定文件的AST
  • 最初,AST 将被剖析;

    • 在这个阶段,以后模块的 依赖关系(如其余模块)将被确定,webpack 能够检测其它的性能(如require.contextmodule.hot)等;
    • AST 剖析产生在 JavascriptParser 中, 这个过程的一部分是最重要的,因为 打包过程中接下来的很多事件都取决于这个局部

通过剖析 AST 发现依赖关系


其中 moduleInstance 是指从 index.js 文件中创立的 NormalModule。红色的 dep 是指从第一个 import 语句中创立的依赖关系,蓝色的 dep 是指第二个 import 语句。

当初 AST 曾经被查看过了,到 建设模块树 的过程了。下一步是解决在上一步发现的 依赖关系 。依照上图,index 模块有两个依赖关系,也是模块,即 math.jsutils.js。但 在这些依赖关系成为理论的模块之前,咱们只有 index 模块 ,为了把它们变成模块,咱们须要应用这些依赖关系映射到的ModuleFactory,并反复下面形容的步骤(本节结尾的图中的虚线箭头示意反复)。 在解决完以后模块的依赖关系后,这些依赖关系也可能有依赖关系,这个过程始终继续到没有更多的依赖关系。这就是模块树的建设过程,当然也要确保父模块和子模块之间的连贯被正确设置。

让咱们实现一个 自定义插件 的办法,它将使咱们可能遍历 ModuleGraph。上面是形容模块如何相互依赖的图。


简略的做一个介绍:a.js 文件导入 b.js 文件,b.js文件同时导入 b1.jsc.js,而后 c.js 导入 c1.jsd.js,最初,d.js导入 d1.jsROOT 指的是null 模块,它是 ModuleGraph 的根。

入口选项只包含一个值,即a.js

// webpack.config.js
const config = {entry: path.resolve(__dirname, './src/a.js'),
    /* ... */
};

当初让咱们看看咱们的自定义插件会是什么样子

class UnderstandingModuleGraphPlugin {apply(compiler) {
    const className = this.constructor.name;
    
    compiler.hooks.compilation.tap(className, (compilation) => {compilation.hooks.finishModules.tap(className, (modules) => {
        // 检索 Map
        const {moduleGraph: { _moduleMap: moduleMap},
        } = compilation;

        // 以 DFS 的形式遍历模块图
        const dfs = () => {
          // "模块图" 的根模块是 * 空模块 *。const root = null;

          const visited = new Map();

          const traverse = (crtNode) => {if (visited.get(crtNode)) {return;}
            visited.set(crtNode, true);

            console.log(crtNode?.resource ? path.basename(crtNode?.resource) : 'ROOT'
            );

            // 取得相干的 `ModuleGraphModule`,它只有一些
            // 除了 `NormalModule' 之外的额定属性,咱们能够用它来进一步遍历图形。const correspondingGraphModule = moduleMap.get(crtNode);

            // 通过指定字段构建 `children` 信息
            const children = new Set(
              Array.from(correspondingGraphModule.outgoingConnections || [],
                (c) => c.module
              )
            );
            for (const c of children) {traverse(c);
            }
          };

          // 从 root 节点开始遍历
          traverse(root);
        };

        dfs();});
    });
  }
}

对代码最一个简略的解释

  • 现有的 webpack hooks 中增加逻辑的形式是应用 tap 办法

    • 函数签名为tap(string, callback)
    • 其中 string 次要是为了调试的目标,示意自定义逻辑是由哪个起源增加的。
    • callback的参数取决于咱们要增加自定义性能的hook
  • compilation 对象上:它蕴含大部分打包过程 状态

    • 模块图(module graph)
    • 创立的chunks
    • 创立的modules
    • 生成的assets
    • 以及更多的信息
  • finishModules是在 所有 模块(包含它们的依赖关系和依赖关系的依赖关系等等)构建结束后才被被调用
  • modules是一个蕴含 所有已建模块的汇合

    • 一个 NormalModule 是由 NormalModuleFactory 产生的
  • 模块 mapMap<Module, ModuleGraphModule>

    • 它蕴含了咱们须要的所有信息,以便遍历图
  • DFS的形式遍历模块图
  • ModuleGraphModule,它只有一些除了 `NormalModule’ 之外的额定属性
  • Connection的字段信息

    • ConnectionoriginModule 是箭头开始的中央。
    • Connectionmodule 是箭头的起点。
    • 所以,Connectionmodule 是一个子节点。

  • correspondingGraphModule.outgoingConnections是一个 Set 或者undefined(在节点没有子节点的状况下)。

    • 应用 new Set 是因为 一个模块能够通过多个连贯援用同一个模块
    • 例如,一个 import foo from 'file.js' 将导致 2 个连贯:

      • 一个是简略的导入
      • 一个用于 `foo’ 默认指定器

依据模块的层次结构,失去如下的输入。

a.js
b.js
b1.js
c.js
c1.js
d.js
d1.js

5. Module/Chunk/ ChunkGroup /EntryPoint 是个啥

Module

前文其实曾经解释过了,这里再做一次总结哇。

模块是一个文件的升级版

一个模块,一旦创立和构建,除了 源代码,还蕴含很多有意义的信息,如:

  • 应用的 加载器
  • 它的 依赖关系
  • 它的 进口(如果有的话)
  • 它的 哈希值

Chunk

一个 Chunk 封装了一个或多个模块

个别状况下,entry文件(一个 entry 文件 =entry对象的一个我的项目)的数量与所产生的 Chunk 的数量 成正比

  • 因为 entry 对象可能只有一个我的项目,而后果块的数量可能大于 1。确实,对于每一个 entry 我的项目,在 dist 目录中都会有一个相应的chunk

但也可能是隐式创立其余的 chunk,例如在应用import() 函数时。但不论它是如何被创立的,每个 chunkdist目录下 都会有一个对应的文件

ChunkGroup

一个 ChunkGroup 蕴含一个或多个 chunks

一个 ChunkGroup 能够是另一个 ChunkGroup 的父或子。

  • 例如,当应用 动静导入 时,每应用一个 import() 函数,就会有一个 ChunkGroup 被创立 ,它的父级是一个 现有的 ChunkGroup,即包含应用import() 函数的文件(即模块)的那个。

EntryPoint

EntryPoint 是 ChunkGroup 的一种类型,它是为 entry 对象中的每一个我的项目创立的


构建 ChunkGraph

从整体的流程图上看,ModuleGraph 只是打包过程中的一个必要局部。为了使 代码宰割 等性能成为可能,它必须被利用起来。

在打包过程的这一点上,对于 entry 对象中的每个我的项目,都会有一个 EntryPoint 因为它是 ChunkGroup 的一种类型,它 至多会蕴含一个 chunk。所以,如果 entry 对象有 3 个我的项目,就会有 3 个 EntryPoint 实例 ,每个实例都有一个 chunk,也叫Entrypoint chunk,其名称是entry 我的项目 key 的值。与 entry 文件相关联的模块被称为 entry 模块,它们每个都将属于它们的入口块。它们之所以重要是因为它们是 ChunkGraph 构建过程的 终点 。请留神, 一个 chunk 能够有多个入口模块

// webpack.config.js
entry: {foo: ['./a.js', './b.js'],
},

在下面的例子中,有一个名为 fooentrykey)的 chunk 将有 2 个入口模块:一个与 a.js 文件相干,另一个与 b.js 文件相干。当然,该 chunk 将属于依据 entry 我的项目创立的 EntryPoint 实例

在具体探讨之前,让咱们先列出一个例子,在此基础上探讨构建过程。

entry: {foo: [path.join(__dirname, 'src', 'a.js'), path.join(__dirname, 'src', 'a1.js')],
    bar: path.join(__dirname, 'src', 'c.js'),
  },

这个例子将包含后面提到的货色:ChunkGroups 的父子关系、chunksEntryPoints

ChunkGraph 是以 递归的形式 建设的。它首先将所有的 entry 模块增加到一个 队列 中。而后,当一个 entry 模块被解决时,意味着其依赖关系(也是模块)将被查看,每个依赖关系也将被增加到队列中 。这样始终反复上来,直到队列变空。这个过程的这一部分是模块被 拜访 的中央。然而,这只是 第一局部

ChunkGroups 能够是其余 ChunkGroups 的父 / 子。这些分割在 第二局部 失去解决。例如,如前所述,一个动静导入(即 import() 函数)会产生一个新的子 ChunkGroup。在 webpack 的说法中,import()表达式定义了一个 异步的依赖关系块

当初,让咱们先看看从上述配置中创立的 ChunkGraph 的图表。

从上图中,能够看到有 4 个 chunk,所以会有 4 个 输入文件 foo chunk 将有 4 个模块,其中 2 个是 entry 模块。bar chunk将只有一个 entry 模块,另一个能够被认为是一般模块。每个 import()表达式都会产生一个新的 ChunkGroup(其父级是bar EntryPoint)。

产生的文件的内容是依据 ChunkGraph 来决定的,所以这就是为什么它对整个打包过程十分重要。

ModuleGraph 相似,属于 ChunkGraph 的节点被称为 ChunkGraphChunk,它只是一个 装璜过的 chunk,他们之间的关系如下:WeakMap<Chunk, ChunkGraphChunk>


6. 提交 chunk 资源

所产生的文件并不是原始文件的正本,因为为了实现其性能,webpack 须要增加一些 自定义代码,使所有都按预期工作。

这就引出了一个问题:webpack 如何晓得要生成什么代码?

这所有都从最根本的局部开始:<span style=”font-weight:800;color:#FFA500;font-size:18px”>{模块 | module}</span>。一个模块能够 导出成员 导入其余成员 应用 import() 导入,应用webpack 特定的函数(例如require.resolve)等等。

依据模块的源代码,webpack 能够决定生成哪些代码以实现所需的性能。并且在剖析 AST 时发现对应模块的依赖关系。

例如,从 ./foo 导入 {aFunction} 将导致 两个依赖关系 (一个是 导入语句自身 ,另一个是 指定器,即 aFunction),从中将创立一个模块。

另一个例子是 import() 函数。这将导致一个 异步的依赖关系块,其中一个依赖关系是 ImportDependency,它是动静导入所特有的。

这些 依赖关系是必不可少的,因为它们带有一些对于应该生成什么代码的提醒 。例如,ImportDependency 确切地晓得要通知 webpack 一些信息,以便异步地获取导入的模块并应用其导出的成员。这些提醒能够被称为 运行时申请

总而言之,一个模块会有它的运行工夫要求,这取决于该模块在其源代码中应用的内容。当初,webpack 晓得了一个 chunk 的所有需要,它将可能正确地生成运行时代码。

这也被称为 渲染过程,渲染过程在很大水平上依赖于 ChunkGraph,因为它蕴含 Chunk 组(即 ChunkGroupEntryPoint),这些组蕴含 Chunks,这些 Chunks 蕴含模块,这些模块以一种细化的形式蕴含无关 webpack 将生成的运行时代码的信息和提醒。


后记

分享是一种态度

参考资料:

  • webpacks-bundling-process
  • 效率工程化

全文完,既然看到这里了,如果感觉不错,顺手点个赞和“在看”吧。

本文由 mdnice 多平台公布

正文完
 0