webpack中使用resolve字段来配置模块的相关解析策略。本质上是通过对resolve库的使用来解析引入模块路径,帮助 webpack 找到 bundle 中以require/import引入的模块代码。
模块可以是一个文件也可以是一个文件夹,可以是相对路径, 也可以是绝对路径 ,还可以是模块路径/npm路径(如:import lodash "loadash";)。无论是哪一种情况的引入,最终解析得到的都是一个绝对路径的文件,这个文件可能有扩展名,也可能没有扩展名
- 绝对路径
import '/home/me/file';
import 'C:\Users\me\file';
由于已经获得文件的绝对路径,因此不需要再做进一步解析
- 相对路径
import '../src/file1';
import './file2';
在这种情况下,使用 import 或 require 的资源文件所处的目录,被认为是上下文目录。在 import/require 中给定的相对路径,会拼接此上下文路径,来生成模块的绝对路径
- 模块路径 比较复杂,下面会举例
modules
告诉 webpack 去哪些目录下寻找第三方模块(也就是使用模块路径引入的时候,例如'import lodash from 'lodash''),默认是只会去 node_modules 目录下寻找,是一个数组;
绝对路径和相对路径都能使用,但是之间有一点差异。
- 使用绝对路径,将只在给定目录中搜索。
module.exports = {
resolve: {
modules: ['node_modules']
}
};
- 如果你想要添加一个目录到模块搜索目录,此目录优先于 node_modules/ 搜索:
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'] //从左到右依次查找
}
};
descriptionFiles
这个选项会定义一个用于描述的 JSON 文件,默认为descriptionFiles:['package.json']
这个json里会包括某些描述路径的字段如main、module等,这些字段的值会指定一个具体模块指向的路径。
举例
我们在自己的项目里,需要引入 lodash2 模块;我们进行相关配置
// 本项目
// webpack.config.js
module.exports = {
resolve: {
modules: ['node_modules2'],
descriptionFiles: ['package2.json']
mainFields: ['main2', 'browser', 'main'],
}
};
// 本项目
import lodash2 from 'lodash2';
// lodash2项目
// package2.json
...
"main2":"main2.js"
"browser": "build/upstream.js",
- 首先,通过本项目webpack配置里的
modules: ['node_modules2']字段,会去node_modules2下寻找lodash2 - 发现lodash是个文件夹,再通过本项目webpack配置里的
descriptionFiles:['package2.json']寻找lodash2下的package2.json文件(下面的两步我们可以看完下节mainFields后再理解) - 再通过本项目webpack配置里的
mainFields: ['main2', 'browser', 'main']字段,在loadash2的package2.json中依次寻找main2browsermain字段,看先命中哪一个 - 发现先命中main2字段,那么我们读其值,
main2.js,这个文件也就是import lodash2 from 'lodash2';最终引入的文件
mainFields
当从 npm 包中导入模块时(例如,import D3 from 'd3'),此选项将决定在 package.json 中使用哪个字段导入模块。根据 webpack 配置中指定的 target 不同,默认值也会有所不同。
默认值
当 target 属性设置为 webworker, web 或者没有指定,默认值为mainFields: ['browser', 'module', 'main']
对于其他任意的 target(包括 node),默认值为: mainFields: ['module', 'main']
怎么用
在 package.json 文件里,新增字段并指定导入路径 如:
// package.json
{
...
"browser": "build/upstream.js",
"module": "index.js",
"main":"main.js"
}
Demo
有一些第三方模块会针对不同环境提供几分代码。 例如分别提供采用 ES5 和 ES6 的2份代码,这2份代码的位置写在 package.json 文件里,如下:
{
"jsnext:main": "es/index.js",// 采用 ES6 语法的代码入口文件
"main": "lib/index.js" // 采用 ES5 语法的代码入口文件
}
Webpack会根据mainFields数组的顺序去 package.json 文件里寻找,只会使用找到的第一个。
假如你想优先采用 ES6 的那份代码,可以这样配置:
module.exports = {
//...
resolve: {
mainFields: ['jsnext:main', 'browser', 'module', 'main'],
},
};
这样就会命中 jsnext:main,会优先去 "es/index.js"里寻找
实际应用
Notice
- 如果字段指定的路径文件没有扩展名,如main:'lodash',这时其实还是要用到
extensions做扩展名补充的 - 若 package.json 中找不到
resolve.mainFields中定义的字段,那么这时候会去找resolve.mainFiles选项,此选项就是在 package.json 文件不存在或者 package.json 文件中的mainFields字段没有返回一个有效路径,则按照顺序查找resolve.mainFiles配置选项中指定的文件名,看是否能匹配到一个存在的文件名
mainFiles
当你引入路径只精确到一个文件夹时, 由这个字段来判断到底指向哪个文件, 默认为: index(建议不要修改,就使用index)
import { a } from '../utils'
通常我们这样写就行,并不会指定../utils/index.js,这就是使用了mainFiles字段
extensions
是一个数组,尝试按顺序解析这些后缀名,这样用户在使用时可以省略相关扩展名
module.exports = {
//...
resolve: {
extensions: ['.js', '.json', '.wasm'],
},
};
alias
定义别名来把原导入路径映射成一个新的导入路径
module.exports = {
//...
resolve:{
alias:{
components: './src/components/'
}
}
};
当你通过 import Button from 'components/button' 导入时,实际上被等价替换成了 import Button from './src/components/button'
注:这样做可能会命中太多的导入语句,alias 还支持 $ 符号来缩小范围到只命中以关键字结尾的导入语句:
resolve:{
alias:{
'react$': path.resolve(__dirname, 'src/utilities/')
}
}
注:resolve.alias 优先级高于其它模块解析方式。
aliasFields
指定一个字段进行解析。
module.exports = {
//...
resolve: {
aliasFields: ['browser'],
},
};
不太能get这个作用
Other
-
fallback 当正常解析失败时,重定向模块请求
-
exportsFields 在 package.json 中用于解析模块请求的字段
-
unsafeCache
- 传递
true将缓存一切模块,并 不安全。 - 正则表达式,或正则表达式数组,可以用于匹配文件路径或只缓存某些模块。
module.exports = {
//...
resolve: {
unsafeCache: /src/utilities/,
},
};