深入理解webpack如何解析程式碼路徑

麥芽糖發表於2018-05-18

引入

在運用前端主流框架(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程式碼路徑的童鞋有一定的指引作用~~

相關文章