一、前文回顾
上文详细讲述了 webpack 中的 resolver 的由来及作用:
-
webpack resolver 用于解析webpack 构建过程中的 request 对应的 模块及模块要对应的 loader 的资源路径用的,分为 normal resolver 和 loader resolver 两种类型;
-
NMF.prototype.getResolver 负责获取 resolver,核心是通过 ResolverFactory.prototype.get 方法实现;
-
接着我们讨论了 ResolverFactory 类型的构造函数,重点在于声明钩子:
- 3.1 resolveOptions: 用于修改定制 resolver 行为的配置对象;
- 3.2 resolver:用于修改 resolver 默认行为;
-
详细讨论了 ResolverFactory.prototype.get 方法,内部会优先取用 typedCaches.direct/stringify 缓存,有则返回,没有则调用它 ResolverFactory.prototype._create 方法创建;
-
ResolverFactory.prototype._create 方法内部主要是触发在 WebpackOptionsApply 中配置的 对应 type 的 resolver 选项对象,然后调用 ehanced-resolve 库提供的 Factory.create 方法创建 resolver;接着触发 resolverFactory.hooks.resolver 修改 resolver 的行为并且举例 ResolverCachePlugin;
以上是有关 webpack 中的 Resolver 相关的基础知识, resolve 是 webpack 中相当重要的一个组成部分,后面的几篇文章我们重点研究这个对象。今天这篇我们要研究 webpack 有关 resolver 的配置部分。
二、resolve 配置项和 resolver
在 webpack 中,我们通过 webpack.config.js.resolve、resolverLoader 两个配置对象,其中 resolve 属性用于配置常规的模块的解析器行为,resolveLoader 用于设置 loader 的解析器行为。
二者的配置子项相同,这里我们以 resolve 的配置为例!
2.1 resolve.alias
这个配置大家很常用,一般在开发中用于简化模块的导入路径,比如我们 src/components/ 目录,为了简化我们把他设置为 @:
ini
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
@: path.resolve(__dirname, 'src/components/')
},
},
};
经过这个后,我们可以用如下的方式进行导入:
js
import someText from '@/someText.vue'
该特性由 enhanced-resolve 库中的 AliasPlugin 实现!
2.2resolve.aliasFields
译为"别名字段",也就是用着个指定某个构建目标对应的入口。当然这个特性也是由 AlisPlugin 实现的。
ini
module.exports = {
//...
resolve: {
aliasFields: ['browser'],
},
};
指定一个字段,例如 browser
,根据 此规范进行解析。
我从这个规范中摘抄了一段原文:
The
browser
field is provided by a module author as a hint to javascript bundlers or component tools when packaging modules for client side use.
啥意思嘞?我翻译一波:
browser
字段是由模块的作者提供的,用作指示打包工具(js bundler)或者组件工具所构建产物为客户端使用时,使用该字段指向的模块;(该模块是用在浏览器环境的)
有点拗口是不是,具体的我们后面再介绍 AliasPlugin 时还会再行展开!
2.3 resolve.conditionNames
这个有点意思,但是对于没怎么写过 npm 包或者 SDK 的同学来说有点陌生,这个配置相当于给大家实现了一个条件,最常见的就是区分 ESModule 和 CommonJS;
exports
配置项 (定义一个库的入口)的 conditionName。
我从 node 文档上摘抄了一段:
Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports.
翻译一下就是:conditionalExports 提供了一种根据不同条件映射到不同路径的方式。支持 CommonJS 和 ESM 规范;
比如说,我们有一个包想提供一个给 ESModule 的导出,这个时候我们的 package.json 中要加入这段声明:
json
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
}
}
注意,以上是 Node.js 原生提供的能力,那对于 webpack 而言,也是需要拥有这个能力的,这个工作当然就需要 enhanced-resolve 来完成,因此有了这个配置:
java
module.exports = {
//...
resolve: {
conditionNames: ['require', 'import'],
},
};
当然,要注意,webpack 这个是有优先级的,如 requrire 写在前面,则优先匹配 exports.require;
2.4 resolve.descriptionFiles
这个不新鲜,描述文件,这个一般都是 package.json ,一般也不会去制定它。如果你有这方面的诉求,比如你写了一个 .someDescFile 这种,需要 enhanced-resolve 识别可以设置一下;
2.5 resolve.enforceExtension
如果设置为 true,强制必须写扩展名,不允许写这种:
js
import a from 'a';
2.6resolve.extensionAlias
一个将拓展名与拓展名别名映射的对象。
js
module.exports = {
//...
resolve: {
extensionAlias: {
'.js': ['.ts', '.js'],
'.mjs': ['.mts', '.mjs'],
},
},
};
把 .ts 和 .js 都当 .js 解析;该特性由 ehanced-resolve 内部的 ExtensionAliasPlugin.js 实现。
三、总结
从今天这篇小作文开始我们系统的学习 webpack 内部有关 resolve 的有关配置项目,本文包含以下配置:
- alias: 设置路径别名;
- aliasFields:设置别名字段;
- conditionalName:条件名;
- descriptionFiles:描述文件列表;
- enforceExtension:强制拓展名;
- extensionAlias:扩展名别名;