Webpack 如何解析模块路径

简介: Webpack 如何解析模块路径

前言


你一定见过这些导入方式,无论是 ESM 还是 CommonJS 模块,或是其他模块规范。

import react from 'react'
import button from './components/button'
const path = require('path')

那么 webpack 是如何去解析查找它们的呢?


正文


模块解析


resolver 是一个库(library),用于帮助找到模块的绝对路径。一个模块可以作为另一个模块的依赖模块,然后被后者引用。例如:


import foo from 'path/to/module'


所依赖的模块可以是来自应用程序或者第三方库(library)。resolver 帮助 webpack 找到 bundle 中需要引入的模块代码,这些代码在每个 import/require 语句中。

webpack 使用 enhanced-resolve 来解析文件路径。


解析规则


使用 enhanced-resolve 解析模块,支持三种形式:绝对路径相对路径模块路径


1. 绝对路径


不建议使用。

由于已经取得文件的绝对路径,因此不需要进一步再做解析了。

在实际项目中,除了设置别名 resolve.alias 时采用绝对路径的方式,其他的我几乎没见过使用绝对路径的。(也可能我读的项目太少了)

import button from '/Users/frankie/component/button'


2. 相对路径


在这种情况下,使用 import/require 的资源文件(resource file)所在的目录被认为是上下文目录(context directory)。在 import/require 中给定的相对路径,会添加此上下文路径(context path),以产生模块的绝对路径(absolute path)。

import button from './component/button'


3. 模块路径


上面两种方式,应该没有太多理解难度,而模块名才是我们要重点理解的。

直接引入模块名,首先查找当前文件目录,若查找不到,会继续往父级目录一个一个地查找,直至到项目根目录下的 node_modules 目录(默认)。若再查找不到,则会抛出错误。

import 'react'
import 'module/lib/file'


注意:

  • 默认的 node_modules 可以通过 resolve.modules 进行更改。
  • 查找中会根据 resolve.extensions 自动补全扩展名,默认是 ['.wasm', '.mjs', '.js', '.json']
  • 查找中会根据 resolve.alias 替换掉别名。


模块将在 resolve.modules 中指定的目录内搜索。可以通过 resolve.alias 配置创建一个别名来替换初始模块路径。

一旦上述规则解析路径之后,解析器(resolver)将检查路径是否指向文件目录


  • 指向文件
  1. 如果路径具有文件扩展名,则被直接打包。
  2. 否则,将使用 resolve.extensions 选项作为文件扩展名来解析。
  • 指向目录按以下步骤找到具有正确扩展名的文件:
  1. 如果文件夹中包含 package.json 文件,则按顺序查找 resolve.mainFields 配置选项中指定的字段,并且 package.json 中的第一个这样的字段确定文件路径。
  2. 如果 package.json 文件不存在或者 package.json 文件中 main 字段没有返回一个有效路径,则按顺序查找 resolve.mainFields 配置选项中指定的文件名,看是否能在 import/require 目录下匹配到一个存在的文件名。
  3. 文件扩展名通过 resolve.extensions 选项采用类似的方法进行解析。


若使用 webpack-dev-server 3.x 版本,建议不要随意修改 resolve.mainFields 配置项,它会报错。已确认是 webpack-dev-server 的 bug,将在不久要发布的 4.x 版本修复。详请 #2801


解析与缓存


Loader 解析遵循与文件解析器指定的规则相同的规则。resolveLoader 配置选项可以用来为 Loader 提供独立的解析规则。

每个文件系统访问都被缓存,以便更快触发对同一文件的多个并行或者串行请求。在观察模式下,只有修改过的文件会从缓存中摘出。如果关闭观察模式,在每次编译前清理缓存。


Resolve 配置


该选项用于配置模块如何解析。例如,当在 ES6 中调用 import 'lodash'resolve 选项能够对 webpack 查找 lodash 的方式去做修改。


1. resolve.alias


创建 import 或 require 的别名,来确保模块引入变得更简单。

例如,一些位于 src/ 文件夹下的常用模块:

// webpack.config.js
const path = require('path')
module.exports = {
  //...
  resolve: {
    alias: {
      // 可以是绝对路径,或者是相对路径。
      // 据我不完全观察,结合 path 模块和 __dirname 拼接成“绝对路径”的方案更多。
      // 以下为模糊匹配
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/')
    }
  }
}


现在,你可以这样使用别名了:

import Utility from '../../utilities/utility'
// 别名
import Utility from 'Utilities/utility'


也可以在给定的对象的键后的末尾添加 $,以表示精准匹配。这里不展开赘述,详细请看这里

注意,采用别名引入模块时,先替换后解析。先将模块路径中匹配 alias 中的 key 替换成对应的 value,再做查找。


2. resolve.extensions


自动解析确定的扩展。

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 使用此选项,会覆盖默认数组,默认值:['.wasm', '.mjs', '.js', '.json']。
    // 注意不要少了符号(.),有些人配置不成功,就是因为少了它。
    // 从左到右(从上到下)先后匹配扩展名,选项中没有的后缀,是不会自动补全的。
    extensions: ['.js', '.json']
  }
}


3. resolve.modules


告诉 webpack 解析模块时应该搜索的目录。可以是绝对路径或者相对路径,但是它们之间有一点差异。

通过查看当前目录以及祖先路径(即 ./node_modules../node_modules 等等),相对路径将类似于 Node 查找 node_modules 的方式进行查找。

当使用绝对路径,将只在给定目录中搜索。

// webpack.config.js
const path = require('path')
module.exports = {
  //...
  resolve: {
    // 默认值
    modules: ['node_modules']
    // 添加一个目录到模块搜索目录,此目录优先于 node_modules 搜索。
    modules: [path.resolve(__dirname, 'src'), 'node_modules']
  }
}


一般地,不要去更改该选项。


4. resolve.mainFields


当从 npm 包中导入模块时(例如,import * as D3 from 'd3'),此选项将决定在 package.json 中使用哪个字段导入模块。根据 webpack 配置中指定的 target 不同,默认值也会有所不同。

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 不建议修改
    // target 为 webworker、web 或没有指定时,默认值为:
    mainFields: ['browser', 'module', 'main'],
    // 除去上述几个 target,对于其他任意 target(包括 node),默认值为:
    mainFields: ['browser', 'module', 'main']
  }
}


通常情况下,模块的 package.json 都不会声明 browsermodule 字段,所以便是使用 main 了。(该选项同样不建议更改)


5. resolve.mainFiles


解析目录时要使用的文件名。

当目录中没有 package.json 时,结合 resolve.extensions 来指明使用该目录中哪个文件。


// webpack.config.js
module.exports = {
  //...
  resolve: {
    // 默认值
    // 可添加多个,但不建议修改。
    mainFiles: ['index']
  }
}


尽可能地,不要去修改该选项。因为它同样会影响第三方依赖包解析,可能会导致部分第三方包解析错误。例如,我在验证该配置时,就发现 webpack-dev-server v3 的一个 bug,开发者表示将在 v4 版本中修复。

所以,不建议随意修改的配置包括 modulesmainFieldsmainFiles


6. 更多


它还有其他一些配置项,但比较少用,所以不展开赘述。更多请看这里


ResolveLoader 配置


从 webpack 2 开始,在配置 loader 时强烈建议使用全名。例如 example-loader,以尽可能地清晰。

然而,如果你确实想省略 -loader,也就是说只使用 example,则可以使用 resolveLoader.moduleExtensions 此选项来实现:

// webpack.config.js
module.exports = {
  //...
  resolve: {
    // ...
  }
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
}


我使用 webpack 4 在不配置该选项时,假如将 css-loader 省略为 css,会报错提示找不到 loader。为什么我会单独拿出来介绍一下,因为网上很多文章表示在配置 module.rules 时可以省略 -loader,但我是省略了就不行。所以这里补充一下原因。


小技巧


关于 webpack 默认配置可以从 node_modules/webpack/lib/WebpackOptionsDefaulter.js 查看。


参考


目录
相关文章
|
1月前
|
存储 编译器 BI
掌握Go语言:Go语言基础构建模块解析,优缺点及进销存项目实战(2)
掌握Go语言:Go语言基础构建模块解析,优缺点及进销存项目实战(2)
|
2月前
|
算法 Linux 开发者
CMake深入解析:打造高效动态链接库路径设置
CMake深入解析:打造高效动态链接库路径设置
52 0
|
2月前
|
存储 缓存 算法
Python中collections模块的deque双端队列:深入解析与应用
在Python的`collections`模块中,`deque`(双端队列)是一个线程安全、快速添加和删除元素的双端队列数据类型。它支持从队列的两端添加和弹出元素,提供了比列表更高的效率,特别是在处理大型数据集时。本文将详细解析`deque`的原理、使用方法以及它在各种场景中的应用。
|
4天前
|
存储 API
使用Webpack的module.hot API来定义模块的热替换
使用Webpack的`module.hot` API实现模块热替换,简单示例展示如何在`myModule`变化时执行回调。`module.hot.accept`接收模块路径和回调函数,当模块或其依赖变更时触发回调,用于执行更新逻辑。可通过`module.hot.data`保存和恢复状态以实现热替换时保持应用程序的状态。
|
4月前
|
JavaScript API Windows
Nodejs 文件 与 路径 相关用法实例解析
Nodejs 文件 与 路径 相关用法实例解析
81 0
|
18天前
|
前端开发 JavaScript 开发者
深入了解Webpack:前端模块打包工具
深入了解Webpack:前端模块打包工具
8 1
|
21天前
|
JavaScript IDE 编译器
TypeScript中模块路径解析与配置:深入剖析与最佳实践
【4月更文挑战第23天】本文深入探讨了TypeScript中模块路径解析的原理与配置优化,包括相对路径、Node.js模块解析和路径别名。通过配置`baseUrl`、`paths`、`rootDirs`以及避免裸模块名,可以提升开发效率和代码质量。建议使用路径别名增强代码可读性,保持路径结构一致性,并利用IDE插件辅助开发。正确配置能有效降低维护成本,构建高效可维护的代码库。
|
1月前
|
存储 人工智能 编译器
存算一体新兴力量:解析我国企业在存储创新、技术路径上的多元化探索
存算一体新兴力量:解析我国企业在存储创新、技术路径上的多元化探索
|
1月前
|
数据采集 数据可视化 算法
深入解析ERP系统的业务智能与报表分析模块
深入解析ERP系统的业务智能与报表分析模块
25 3
|
1月前
|
数据挖掘
深入解析ERP系统的人力资源管理模块
深入解析ERP系统的人力资源管理模块
36 1

推荐镜像

更多