一、前文回顾
从今天这篇小作文开始我们系统的学习 webpack 内部有关 resolve 的有关配置项目,本文包含以下配置:
- alias: 设置路径别名;
- aliasFields:设置别名字段;
- conditionalName:条件名;
- descriptionFiles:描述文件列表;
- enforceExtension:强制拓展名;
- extensionAlias:扩展名别名;
今天这篇小作文继续学习 webpack 有关 resolve 和 resolveLoader 的相关配置和 enhanced-resolve 内部的实现信息!
二、resovle 配置项
在 webpack 中,我们通过 webpack.config.js.resolve、resolverLoader 两个配置对象,其中 resolve 属性用于配置常规的模块的解析器行为,resolveLoader 用于设置 loader 的解析器行为。
2.1 resolve.extensions
这个玩意大家也不陌生,用于为不写扩展名的路径 request 尝试添加扩展名,注意这个配置存在优先级,拍在前面的更先解析,如果解析到则不会再解析剩下的;
module.exports = {
//...
resolve: {
extensions: ['.js', '.json']
},
};
比如你写了以下代码:
import a from 'some/dir/a'
ehanced-resolve 会尝试优先解析 some/dir/a.js,如果解析到则返回结果,如果没有才会再尝试解析 some/dir/a.json;
2.2 resolve.fallback
当正常解析失败时,重定向模块请求。这个玩意儿在 webpack5 中十分有用,这是因为 webpack 在v4及以前是自动 pollyfill Node.js 的核心模块的,但是在 v5 放弃了这个特性。
因此,如果你项目中需要这些 Node.js 的特性模块,你需要手动指定,此时用到了这个特性。
module.exports = {
//...
resolve: {
fallback: {
assert: require.resolve('assert'),
buffer: require.resolve('buffer'),
console: require.resolve('console-browserify'),
constants: require.resolve('constants-browserify'),
zlib: require.resolve('browserify-zlib'),
// .....
},
},
};
enhanced-resolve 中由 AliasPlugin.js 插件是现在这个特性;
2.3 resolve.mainFields
当从 npm 包中导入模块时(例如,import * as D3 from 'd3'),mainFields 字段决定使用 package.json 中哪个字段声明的模块。
根据 webpack 配置中指定的 target 不同,默认值也会有所不同。
module.exports = {
//...
resolve: {
mainFields: ['browser', 'module', 'main'],
},
};
比如,target 为 web 时,优先取用 browser 声明的模块;该特性由 MainFieldPlugin 插件实现;
2.4 resolve.mainFiles
request 写目录时自动映射到目录下的兜底文件,一般都是 index.js。这个特性也是 enhanced-resolve 实现的,当然可以通过这个声明修改;
module.exports = {
//...
resolve: {
mainFiles: ['nb'],
},
};
这样,当你写 import a from 'a/' 时,就不会解析 a/index.js 了,而是解析 a/nb.js。 该特性由 UseFilePlugin 插件实现,后面讲插件时咱们展开讲这部分;
2.5 resolve.exportsFields
解析某一个包内的模块请求的,这个玩意儿可以改写 main 字段,还可以支持 some-pkg/sub/pkg 这种解析的映射。
module.exports = {
//...
resolve: {
exportsFields: ['exports', 'myCompanyExports'],
},
};
该特性由 SelfReferencePlugin 插件实现;
2.6 resolve.modules
告知 webpack 解析模块的目录,默认就是 node_modules。如果手动指定这样的一个目录,则你指定的这个目录的优先级高于默认的 node_modules 目录。
该特性内部有 ModulesInHierarchicalDirectoriesPlugin 和 PnpPlugin 插件实现;
2.7 resolve.unsafeCache
缓存表示,若设置为 true 则默认缓存解析到的模块结果。该特性内部由 UnsafeCachePlugin 插件实现。
2.8 resolve.useSyncFileSystemCalls
该选项置为 true 则使用同步的文件系统调用,一般我们不会这么干!
2.9 resolve.plugins
用户创建的 Resolver 插件,和 webpack 插件系统一样,也是一个有 apply 的对象;resolver 的插件自然是订阅 resolver 的各种钩子,然后接入到整个 resolve 的过程中的。
在 Resolver 创建时,需要等到 Resolver 完成内部的 hook 注册之后应用用户定义的插件。
2.10 ### resolve.preferRelative
当启用此选项时,webpack 更倾向于将模块 request 理解析为相对于当前目录的相对请求,而不是去 node_modules 目录下查找。
module.exports = {
//...
resolve: {
preferRelative: true,
},
};
比如我们导入 vue
import Vue from 'vue'
启用 preferRelative 之后,'vue' 等效于 './vue'。当然,一般我们并不需要这个特性。在 enhanced-resolve 内部由 JoinRequestPlugin 插件实现该特性。
2.11 resolve.preferAbsolute
启用该特性时,解析时 偏好的的绝对路径为 resolve.roots
选项指定的目录。
同样也是由 JoinRequestPlugin 实现,一般不会用这个玩意儿。
2.12 resolve.symlinks
是否把解析到的符号链接(软链)解析为其真正的路径,若设置,则会将符号链接解析到源路径。
2.13 resolve.cachePredicate
缓存的断言函数,用于决定一个 request 是否应该被缓存,函数返回 true 标识则缓存;
module.exports = {
//...
resolve: {
cachePredicate: ({ path, request }) => {
// additional logic
return true;
},
},
};
注意,该函数必须返回一个布尔值!
2.14 resolve.restrictions
解析限制条件数组,用于限制被解析的 request 符合一定的规则。
module.exports = {
//...
resolve: {
restrictions: [/.(sass|scss|css)$/],
},
};
比如说我们想限制被解析的 request 需要是 CSS 样式文件,就可以加入这些限制,该特性在 enhanced-reesolve 内部由 RestrictionsPlugin 插件实现。
2.15 resolve.roots
该选项对应一个目录数组,用于解析服务器相对 URL(以“/”开头),默认为 context 配置选项。在非 Windows 系统上,这些请求首先被解析为绝对路径。
2.16 resolve.importsFields
一个字段,用于提供给 某些包的内部 request(以 # 开头的叫做内部请求)。这个值可坑爹了,但是没啥卵用。后面会详细介绍这个东东。
注意,这个选项在 webpack 中文网站上有错误,该字段的值不是 [browser, module, node]
module.exports = {
//...
resolve: {
importsFields: ['imports'],
},
};
那你的包的 package.json 就应该声明这个内容:
{
"name": "qiang-sheng-group",
"main": "./lib/gaoqiqiang.js",
"imports": {
"#xiaohu": "./lib/tangxiaohu.js",
"#xiaolong": "./lib/tangxiaolong.js"
}
}
该特性由 ImportsFieldPlugin 插件实现!
2.17 resolve.byDependency
通过 module 的 request 类型来配置 resolve 选项,比如我们可以有以下方式:
module.exports = {
// ...
resolve: {
byDependency: {
// ...
esm: { // request 是 ESM
mainFields: ['browser', 'module'],
},
commonjs: { // request 是 CommonJS
aliasFields: ['browser'],
},
},
},
};
三、总结
算上前面的一篇介绍 resolve 配置项的文章,一共两篇文章介绍了 webpack.config.js.resolve 和 resolveLoader 选项对象的各个配置项及其作用,期间我们还穿插了一些 ehanced-resolve 的内部实现细节。
下一篇我们正式进入到 createResolver 的重点环节 —— 流水线钩子的注册!