16

resolve.alias

resolve.alias 用于给模块路径指定别名。

为什么要给模块路径取别名呢?

假设在我们的源码中有如下 import 语句:

import BaseModel from '../../../../common/BaseModel';

export default class ProductModel extends BaseModel {

    // some code here
    
}

那么每次引入 BaseModel 的时候,很可能都会面临着写很长一堆 ../../../../ 的问题,而且如果没有编辑器的智能提示,很容易少写(或者多写)一层 ../

此时就可以在 webpack 配置中为 BaseModel 指定一个别名:

{
    // some other configs
    
    resolve: {
        alias: {
            common: require('path').resolve(__dirname, '../src/common')
        }
    }
    
    // some other configs
}

这样一来,引入 BaseModel 的代码就变成下面这样了:

import BaseModel from 'common/BaseModel';

另外一种场景,就是去掉路径中的无意义的一层(站在使用者角度来说无意义)。比如安装了 vue 模块之后,如果不做任何配置,引入 vue 的代码看起来是这样的:

import Vue from 'vue/dist/vue.esm.js';

看起来多多少少会觉得别扭,而且还不好调整成不同的版本(开发时用 vue.esm.js ,发布的时候用 vue.runtime.js )。

此时在 webpack 中加上不同环境的别名:

{
    // some other configs
    
    resolve: {
        alias: {
            vue$: process.env.NODE_ENV === 'production'
                ? 'vue/dist/vue.runtime.js' : 'vue/dist/vue.esm.js'
        }
    }
    
    // some other configs
}

看起来就优雅很多了。

那么 alias 的原理是怎么样的呢?

配置了 alias 之后,在 webpack 解析引入(通过 import 或者 require )的模块的时候,会先将源码中的模块路径中匹配 alias 里 key 的部分替换成 value 部分,再做查找。

比如源码中有如下引入语句:

import Test1 from 'xyz/file';

alias 中有如下配置:

{
    // some other configs
    
    resolve: {
        alias: {
            xyz: './dir'
        }
    }
    
    // some other configs
}

在解析路径的时候,会先将 xyz 替换成 ./dir ,那么之前的 import 语句就相当于:

import Test1 from './dir/file.js';

然后 webpack 再基于 ./dir/file.js 去查找需要引入的模块。

当然,也可以配置绝对路径:

{
    // some other configs
    
    resolve: {
        alias: {
            xyz: require('path').resolve(__dirname, '../dir')
        }
    }
    
    // some other configs
}

依然按照之前先替换后解析的流程执行。

另外,alias 还有一种特殊的语法:key 的末尾带一个 $ 字符,表示精确匹配。

假设有下面 alias 配置:

{
    // some other configs
    
    resolve: {
        alias: {
            xyz$: 'xyz/dir'
        }
    }
    
    // some other configs
}

对于:

import 'xyz/file.js';

这种 import 语句,就无法匹配上这条 alias 规则。

而:

import 'xyz';

才能匹配上。

更多 alias 的匹配示例,参考官网文档

resolveLoader.modules

可以通过 resolveLoader.modules 配置在哪些目录下查找 loader ,默认是在 node_modules 目录下查找。

那么问题就来了,这个默认的 node_modules 指的是哪里的 node_modules 目录呢?换句话说,这里的 node_modules 目录对应的绝对路径是怎么构造的?

webpack 会以当前进程目录( process.cwd() )开始,逐层往上查找 node_modules 目录,如果查到根目录,还没找到,就抛出错误。这与 Node 查找 node_modules 目录的行为是一致的,只不过 Node 是从当前模块所在目录开始查找的。

对于其他的相对目录配置,查找逻辑与默认的 node_modules 一样

对于绝对路径,就直接找这个路径对应的目录了。

权威说明,可参考官网文档

找到 resolveLoader.modules 的具体目录之后,就按照配置的顺序去查找 loader 了。

假设有如下配置:

{
    // some other configs
    
    resolveLoader: {
        modules: ['loaders1', 'loaders2']
    }
    
    // some other configs
}

如果当前进程目录是 /a/b/c ,现在要查找 babel-loader ,就会按照如下顺序查找:

/a/b/c/loaders1/babel-loader/...
/a/b/c/loaders2/babel-loader/...

/a/b/loaders1/babel-loader/...
/a/b/loaders2/babel-loader/...

/a/loaders1/babel-loader/...
/a/loaders2/babel-loader/...

/loaders1/babel-loader/...
/loaders2/babel-loader/...

注:上述示例省略号后面的内容根据其他配置确定,具体参看官网文档,此处不赘述。

Rule.include 、 Rule.exclude 等路径配置

Rule 中的 testincludeexclude 的值都是 Condition 实例。

Condition 实例可以使下面的某一种值:

  • 一个字符串:输入值必须以该字符串开始。
  • 一个正则表达式。
  • 一个由 Condition 实例组成的数组。
  • 一个对象:必须匹配所有属性,每一个属性的行为都是预先定义好的(属性 key 只能是 andor 或者 not )。

对于 Rule.test ,值只能是一个正则表达式或者一个正则表达式数组。

对于 Rule.include ,值只能是一个字符串或者一个字符串数组。

Rule.exclude 的值和 Rule.include 一样。

更详细的描述,参考官网文档

所以,Rule.includeRule.exclude 配置都会使用绝对路径。

entry

entry 中配置的相对路径,是相对于 process.cwd() 去查找的。

output.path

必须配置成一个绝对路径。

babelrc 中的 pluginspresets 路径

babelrc 中 pluginspresets 配置的相对路径是相对于待转换文件解析的。

比如在转换 /a/b/c/d.js 模块的时候,查找 babel-plugin-veui 的顺序是:

/a/b/c/node_modules/babel-plugin-veui/...
/a/b/node_modules/babel-plugin-veui/...
/a/node_modules/babel-plugin-veui/...
/node_modules/babel-plugin-veui/...

presets 的解析逻辑与 plugins 一致。


yibuyisheng
480 声望3 粉丝

前端工程师