阅读 5147

深入理解webpack如何解析代码路径

引入

在运用前端主流框架(react,vue,angular)开发过程中,引入一个模块或文件路径,是如何解析到目标文件的?是按照什么样的规则和顺序查找到的?这个问题乍一看是简单,尤其在小型项目中,模块和文件并不多的情况下。但遇到一些比较复杂的项目,模块和文件路径的引入需要谨慎小心,这就需要深入地理解一下webpack的代码路径解析规则了。

路径

首先,综合一下在项目中配置路径的三种形式

  1. 相对路径

相对路径是相对于当前目录的路径

import {button} from '../component/button'
复制代码
  1. 绝对路径

绝对路径直接指明了文件的具体位置,直接可以查找到(不建议使用)

import {button} from '/home/me/file'
复制代码
  1. 模块名

直接引入模块名,会查找当前文件目录,父级目录直至根目录下的 node_modules(默认) 文件夹,看是否有对应名称的模块。

import React from 'react'
import "module/lib/file";
复制代码

注意:默认的node_modules可以根据webpack配置的resolve.modules进行更改

注意:查找中会根据webpack配置的resolve.extensions自动补全扩展名

注意:查找中会根据wepack配置的resolve.alias替换掉别名

路径解析

根据上述规则解析路径后,解析器将路径指向文件或者文件夹(目录)

  1. 如果是文件,直接加载

  2. 如果是文件夹,查找里面是否有package.json文件

    1)如果有,默认按照里面的main字段的文件名查找文件 (可以通过resolve.mainFields 配置更改)

    2)如果没有,默认查找index.js文件(可以通过resolve.mainFiles配置更改)

resolve配置

了解上述的路径形式和最终的解析规则后,根据我的一些标识,大致上也能够看出webpack路径配置的发挥空间了。

webpack解析代码路径的核心模块是enhanced-resolve模块

webpack解析匹配代码路径的配置在resolve里面

接下来将着重对resolve中alias,extensions,modules,mainFields,mainFiles属性做单独详细的介绍。

resolve.alias

alias,顾名思义,是指路径的别名。简单点说,就是用一个简单的别名来替换一个常用的或者复杂的文件路径。

原理:先替换,后解析。在引入模块时,先将模块路径中匹配alias中的key替换成对应的value,再做查找。

注意一下几点:

  1. 替换掉的路径可以是相对路径,也可以是绝对路径。
resolve: {
    alias: {
      '@': path.resolve(__dirname, 'components'),  
      // 这里使用 path.resolve 和 __dirname 来获取绝对路径
      // import '@/Button.js' 等同于 import '[项目绝对路径]/components/Button.js'
      x: './src/components'   // 相对路径
    }
}
复制代码

2.这里的匹配是模糊匹配,不是精确匹配。即路径中携带别名即可匹配

3.注意精确匹配的用法。在key的末尾带一个$字符

resolve: {
    alias: {
        'vue$': 'vue/dist/vue.esm.js',
        'x$': 'module/dir'
        // Import 'x/file.js'  // 这里不能匹配
    }
}
复制代码

resolve.extensions

extensions中指定了webpack可以识别的文件扩展名。

注意一下几点:

  1. 扩展名匹配的先后顺序。从左到右依次匹配。比如下例中,引入import {Button} from './component/button',优先匹配的是button.js,如果没有button.js,会匹配button.vue,依次往下。
  2. extensions中没有的后缀,是不会自动补全的。比如下例中没有css的扩展名,如果想引入import {Button} from './style/button',是匹配不到button.css文件的
resolve: {
    extensions: ['js', 'vue', 'json']
}
复制代码

resolve.modules

查找声明依赖名的模块,默认搜索node_modules目录。一般我们不修改这个配置。

resolve: {
  modules: ['node_modules']
},
复制代码

resolve.mainFields

在引用模块时,指明使用package.json中哪个字段指定的文件,默认是“main”

resolve: {
  // 配置 target === "web" 或者 target === "webworker" 时 mainFields 默认值是:
  mainFields: ['browser', 'module', 'main'],

  // target 的值为其他时,mainFields 默认值为:
  mainFields: ["module", "main"],
}
复制代码

因为通常情况下,模块的 package 都不会声明 browser 或 module 字段,所以便是使用 main 了。

resolve.mainFiles

在目录中没有package.json时,指明使用该目录中哪个文件,默认是index.js

resolve: {
  mainFiles: ['index'], // 可以添加其他默认使用的文件名
}
复制代码

最后

resolve除了上面介绍的几个属性外,还有其他一些属性,有兴趣的筒子们可以再深入研究一下。希望我这篇文章对还不太清楚webpack代码路径的童鞋有一定的指引作用~~