🗒️module

1. modules

1.1 模块类型

webpack 特性之一就是能通过 import 导入任何类型的模块,它支持的模块类型:

webpack 还支持使用多种语言编写的 module 及其相应的 loader 预处理器。

loader 向 webpack 描述如何处理 non-native 的 module 以及如何将这些 dependencies 包含到我们的 bundles 里。webpack 社区已经为各种流行的语言和语言处理器构建了 loaders(常见的如下),更多详见 loader 列表自定义 loader。

  • ESNext (Babel) / TypeScript / CoffeeScript

  • Sass / Less / Stylus

  • Elm

每个 module 都有其对应的 loader

1.2 模块依赖关系

webpack modules 能以各种方式表达它们的依赖关系,比如:

  • ES6 import 语句

  • CommonJS require() 语句

  • AMD definerequire 语句

  • css/sass/less 文件里的 @import 语句

  • 图片的 url 地址:样式表 url(...), HTML <img src=...>

1.3 模块打包

为了更好地理解 module bundlers 背后的思想及其底层原理,可以进一步阅读:

2. 模块解析

resolver 是一个库,可以通过 module 的绝对路径来定位 module。

一个 module 可以依赖另一个 module。所依赖的 module 可以是来自应用程序的代码,也可以来自第三方库。

import foo from 'path/to/module';
// or
require('path/to/module');

resolver 会帮 webpack 为每个这样的 require/import 语句找到要包含在 bundle 中的 module 代码。在打包模块(bundle modules)时,webpack 使用 enhanced-resolve 来解析文件路径。

2.1 解析规则

使用 enhanced-resolve,webpack 可以解析三种文件路径:

// 1. 绝对路径
import '/home/me/file';
import 'C:\\Users\\me\\file';

// 2. 相对路径
import '../src/file1';
import './file2';

// 3. 模块路径
import 'module';
import 'module/lib/file';

重点介绍下模块路径。

  1. 解析路径

    • resolve.modules 指定的所有目录中搜索 modules,也可以使用 resolve.alias 选项将原始模块路径替换为备用路径

    • 按顺序查找 resolve.exportsFields 指定的字段,package.json 中的第一个此类字段决定可用的 exports(根据 package exports guideline

  2. resolver 检查路径是文件还是文件夹:

    • 如果路径指向文件

      • 如果有扩展名,则直接打包该文件

      • 否则就使用 resolve.extensions 指定的扩展名来解析,例如 .js, .jsx

    • 如果路径指向文件夹,则根据以下步骤找到具有正确扩展名的正确文件

      • 按顺序查找 resolve.mainFields 指定的字段,package.json 中的第一个此类字段决定文件路径

      • 如果没有 package.jsonresolve.mainFields 不是有效路径,则按顺序查找 resolve.mainFiles 指定的文件名,看在该目录下是否存在匹配的文件名

      • 解析文件扩展名,用类似 resolve.extensions 的方式

webpack 会根据我们的构建目标为这些选项提供合理的默认值,详见 Configuration/Resolve

2.2 解析 loader

loader 的解析规则和文件解析的规则相同。只是 resolveLoader 配置选项可以为 loader 设置独立的解析规则,详见 Configuration/Resolve/#resolveloader

2.3 缓存

通常,文件系统的访问都会被缓存。在 watch 模式下,只有修改过的文件才会从缓存中移除。如果关闭 watch 模式,则会在每次编译之前清除缓存。

更多配置信息,可查阅 Resolve API (Configuration/Resolve)

3. 模块联盟

多个独立的 builds 应该形成一个应用程序。这些单独的 build 之间不应该有依赖关系,因此它们可以单独开发和部署。

模块分为:

  1. local modules,本地模块:就是普通模块,它是当前 build 的一部分。

  2. remote modules,远程模块:不属于当前 build,它是在运行时从所谓的 container 里加载的

    • 加载远程模块是个异步操作,它们会放在远程模块和入口之间的下一个 chunk 的加载操作中。没有 chunk 的加载操作,就不可能使用远程模块。chunk 的加载操作通常是使用 import() 调用的,但也支持像 require.ensurerequire([...]) 这样的旧语法。

    • container 是通过 container 入口创建的,该入口公开了对特定 modules 的异步访问,分两个步骤:加载模块(异步)、执行模块(同步)

    • container 可以嵌套使用。container 可以使用来自其它 container 的 module。container 之间也可以循环依赖

每个 build 都可以充当 container,并将其它 build 作为 container 使用。这样每个 build 都可以访问其它 container 公开的 module。

相关说明:

  1. import 和 export 任何 webpack 支持的 module 类型

  2. chunk 加载应该是并行的

  3. 从 consumer 到 container 的控制

    • 覆盖 module 是一种单向操作

    • 同级 container 不能覆盖彼此的 module

  4. 共享中的相对请求和绝对请求

    • 会一直提供,即使不使用

    • 会相对于 config.context 进行解析

    • 默认不使用 requiredVersion

  5. 共享中的 module 请求

    • 仅在使用时提供

    • 会匹配在 build 中使用的所有相等 module 请求

    • 会提供所有匹配的 modules

    • 会从 package.json 中提取 requiredVersion,在 graph 中的这个位置

    • 可以提供和使用多个不同的版本,当有嵌套的 node_modules 时

  6. 共享中的以 / 结尾的 module 请求,会匹配所有具有此前缀的 module 请求

更多内容,可查看 Concepts/Module Federation

Last updated