相依性解析

當 Parcel 建置您的原始碼時,它會發現相依性,讓程式碼可以拆分為個別檔案,並在多個地方重複使用。相依性會說明如何尋找包含您依賴程式碼的檔案,以及如何建置它的相關資訊。

相依性指定符

#

相依性指定符是一個字串,它說明相依性相對於匯入它的檔案的位置。例如,在 JavaScript 中,import 陳述式或 require 函式可用於建立相依性。在 CSS 中,可以使用 @importurl()。通常,這些相依性不會指定完整的絕對路徑,而是由 Parcel 和其他工具解析為絕對路徑的較短指定符。

Parcel 實作了 Node 模組解析演算法 的加強版本。它負責將相依性指定符轉換為可以從檔案系統載入的絕對路徑。除了許多工具支援的標準相依性指定符外,Parcel 還支援一些額外的指定符類型和功能。

相對指定符

#

相對指定符以 ... 開頭,並解析相對於匯入檔案的檔案。

/path/to/project/src/client.js
import './utils.js';
import '../constants.js';

在上面的範例中,第一個 import 會解析為 /path/to/project/src/utils.js,而第二個會解析為 /path/to/project/constants.js

檔案副檔名

#

建議在所有 import 規範符中包含完整的檔案副檔名。這可以同時改善相依性解析效能並減少歧義。

話雖如此,為了相容於 Node 中的 CommonJS 和 TypeScript,Parcel 允許省略某些檔案類型的檔案副檔名。可以省略的檔案副檔名包括 .ts.tsx.mjs.js.jsx.cjs.json。要匯入所有其他檔案類型,則需要檔案副檔名。

以下範例解析為與上面相同的檔案。

/path/to/project/src/client.js
import './utils';
import '../constants';

請注意,只有從 JavaScript 或 TypeScript 檔案匯入時才能省略這些檔案副檔名。在 HTML 和 CSS 等其他檔案類型中定義的相依性,其檔案副檔名始終是必需的。

在 TypeScript 檔案中,Parcel 也會嘗試將包括 .js.jsx.mjs.cjs 在內的 JavaScript 副檔名替換為它們的 TypeScript 等效副檔名 (.ts.tsx.mts.cts)。例如,對 ./foo.js 的相依性會解析為 ./foo.ts。這與 TSC 的行為相符。但是,與 TSC 不同的是,如果原始 ./foo.js 存在,它將會用於取代 TS 版本,這與 Node 和其他打包器的行為相符。

目錄索引檔案

#

在 JavaScript、Typescript 和其他基於 JS 的語言中,相依性規範符可能會解析為目錄,而不是檔案。如果目錄包含 package.json 檔案,則主程式會如 套件條目 區段所述進行解析。如果沒有 package.json,它會嘗試解析為目錄內的索引檔案,例如 index.jsindex.ts。上述所有副檔名都支援索引檔案。

/path/to/project/src/app.js
import './client';

例如,如果 /path/to/project/src/client 是目錄,則上述規範符可以解析為 /path/to/project/src/client/index.js

裸規範符

#

裸露的指定符號以任何字元開頭,但下列字元除外:./~#。在 JavaScript、TypeScript 和其他基於 JS 的語言中,它們會解析為 node_modules 中的套件。對於其他類型的檔案,例如 HTML 和 CSS,裸露的指定符號會以與 相對指定符號 相同的方式處理。

/path/to/project/src/client/index.js
import 'react';

在上述範例中,react 可能解析為類似 /path/to/project/node_modules/react/index.js 的內容。確切位置會取決於 node_modules 目錄的位置,以及套件中的組態。

node_modules 目錄會從匯入檔案向上搜尋。搜尋會在專案根目錄停止。例如,如果匯入檔案位於 /path/to/project/src/client/index.js,則會搜尋下列位置

找到模組目錄後,就會解析套件項目。請參閱 套件項目 以取得有關此程序的更多詳細資訊。

套件子路徑

#

裸露的指定符號也可以指定套件內的子路徑。例如,套件可能會發布多個進入點,而不仅仅只發布一個進入點。

import 'lodash/clone';

上述範例會如上所述在 node_modules 目錄中解析 lodash,然後解析套件中的 clone 模組,而不是其主要進入點。例如,這可能是 node_modules/lodash/clone.js 檔案。

內建模組

#

Parcel 包含許多內建 Node.js 模組的 shim,例如 pathurl。當依賴項指定符號參照其中一個模組名稱時,會優先使用內建模組,而不是 node_modules 中安裝的具有相同名稱的任何模組。在為節點環境建置時,會從套件中排除內建模組,否則會包含 shim。請參閱 Node 文件 以取得內建模組的完整清單。

在為 Electron 環境建置時,electron 模組也會被視為內建模組,並從套件中排除。

絕對規格

#

絕對規格以 / 開頭,並解析相對於專案根目錄的檔案。專案根目錄是專案的基本目錄,通常會包含套件管理員鎖定檔(例如 yarn.lockpackage-lock.json),或原始碼控制目錄(例如 .git)。絕對規格可用於避免在深度巢狀層級中出現非常長的相對路徑。

import '/src/client.js';

上述範例可以放在專案目錄結構中的任何檔案中,任何位置,且永遠會解析為 /path/to/project/src/client.js

波浪符號規格

#

波浪符號規格以 ~ 開頭,並解析相對於匯入檔案中最近的套件根目錄。套件根目錄是一個有 package.json 檔案的目錄,通常會在 node_modules 中找到,或作為單一儲存庫中套件的根目錄。波浪符號規格可用於與絕對規格類似的目的,但當您有多個套件時會更有用。

/path/to/project/packages/frontend/src/client/index.js
import '~/src/utils.js';

上述範例會解析為 /path/to/project/packages/frontend/src/utils.js

雜湊規格

#

雜湊規格以 # 字元開頭,而行為取決於它們所包含的檔案類型。在 JavaScript 和 TypeScript 檔案中,雜湊規格會被視為內部套件匯入,如下所述。在其他檔案中,這些會被視為相對 URL 雜湊。

package.json 中的 "imports" 欄位可用於定義套件中 JavaScript 或 TypeScript 檔案中匯入規格適用的私人對應。這允許套件定義有條件的匯入,具體取決於環境,如下所述 文件Node.js 文件 中所述。

/path/to/project/package.json
{
"imports": {
"#dep": {
"node": "dep-node",
"browser": "dep-browser"
}
}
}
/path/to/project/src/index.js
import '#dep';

查詢參數

#

相依性指定符號也可以包含查詢參數,用於指定已解析檔案的轉換選項。例如,您可以在載入影像時指定影像的寬度和高度以調整大小。

.logo {
background: url(logo.png?width=400&height=400);
}

請參閱影像轉換器文件以取得更多關於影像的詳細資訊。您也可以在自訂轉換器外掛程式中使用查詢參數。

注意:查詢參數不支援 CommonJS 指定符號(由 require 函式建立)。

URL 架構

#

相依性指定符號可以使用 URL 架構來鎖定命名管線。這些允許您指定與預設管線不同的管線來編譯檔案。例如,bundle-text: 架構可以用於將編譯好的套件內嵌為文字。請參閱套件內嵌以取得更多詳細資訊。

有幾個保留的 URL 架構可能無法用於命名管線,且具有內建行為。

Glob 指定符號

#

Parcel 支援透過 glob 一次匯入多個檔案,然而,由於 glob 匯入是非標準的,因此未包含在預設 Parcel 設定中。若要啟用它們,請將 @parcel/resolver-glob 加入您的 .parcelrc

.parcelrc
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}

啟用後,你可以使用 ./files/*.js 之類的指定符號匯入多個檔案。這會傳回一個物件,其金鑰對應到檔案名稱。

import * as files from './files/*.js';

等同於

import * as foo from './files/foo.js';
import * as bar from './files/bar.js';

let files = {
foo,
bar
};

具體來說,glob 模式的動態部分會變成物件的金鑰。如果有多個動態部分,將會傳回一個巢狀物件。例如,如果存在 pages/profile/index.js 檔案,下列範例會與其相符。

import * as pages from './pages/*/*.js';

console.log(pages.profile.index);

這也適用於 bundle-text: 等 URL 架構,以及動態匯入。使用動態匯入時,產生的物件會包含檔案名稱對應到函式的對應。每個函式都可以呼叫來載入已解析的模組。這表示每個檔案都是依需求載入,而不是全部預先載入。

let files = import('./files/*.js');

async function doSomething() {
let foo = await files.foo();
let bar = await files.bar();
return foo + bar;
}

Glob 也可用於從 npm 套件匯入檔案

import * as locales from '@company/pkg/i18n/*.js';

console.log(locales.en.message);

Glob 匯入也適用於 CSS

@import "./components/*.css";

等同於

@import "./components/button.css";
@import "./components/dropdown.css";

套件項目

#

解析套件目錄時,會諮詢 package.json 檔案來判斷套件項目。Parcel 會依序檢查下列欄位

如果未設定這些欄位,或這些欄位指向的檔案不存在,解析就會回退到索引檔案。詳情請參閱 目錄索引檔案

套件匯出

#

package.json 中的 "exports" 欄位可用於定義套件的公開存取進入點。這些欄位也可以根據環境定義條件式行為,允許解析根據模組匯入來源位置(例如節點或瀏覽器)而變更。

啟用套件匯出

#

套件匯出預設為停用,因為它們可能會中斷未考量到它們的現有專案。您可以在專案根目錄的 package.json 檔案中新增下列內容來啟用支援。

/path/to/project/package.json
{
"@parcel/resolver-default": {
"packageExports": true
}
}

單一匯出

#

如果套件只有一個匯出的模組,"exports" 欄位可以定義為字串

package.json
{
"name": "foo",
"exports": "./dist/index.js"
}

當使用者在上述範例中匯入 "foo" 套件時,將會解析 node_modules/foo/dist/index.js 模組。

多重匯出

#

如果套件匯出多個模組,"exports" 欄位可以提供對應,定義如何尋找這些匯出。"." 匯出定義主要進入點,其他進入點則定義為子路徑。

package.json
{
"name": "foo",
"exports": {
".": "./dist/index.js",
"./bar": "./dist/bar.js"
}
}

使用上述套件時,使用者可以匯入 "foo",解析為 node_modules/foo/dist/index.js,或匯入 "foo/bar",解析為 node_modules/foo/dist/bar.js

任何未明確由包含 "exports" 欄位的套件匯出的子路徑,消費者都無法存取。例如,嘗試在上述套件中匯入 "foo/abc" 會導致建置時間錯誤。

* 字元可用於匯出對應中的萬用字元。對應的左方只會出現一個 *,而對應的字串會取代右方中的每個執行個體。這可讓您避免手動列出每個您要匯出的檔案。

package.json
{
"name": "foo",
"exports": {
"./*": "./dist/*.js"
}
}

在上述範例中,dist 目錄中的所有 .js 檔案都會從套件中匯出,不含副檔名。例如,匯入 "foo/bar" 會解析為 node_modules/foo/dist/bar.js

條件式匯出

#

"exports" 欄位也可以在不同的 環境 或其他條件中,為相同的指定符號定義不同的對應。例如,套件可以為 ES 模組和 CommonJS,或 Node 和瀏覽器提供不同的進入點。

package.json
{
"name": "foo",
"exports": {
"node": "./dist/node.js",
"default": "./dist/default.js"
}
}

在上述範例中,如果消費者從 Node 環境匯入 "foo" 模組,它會解析為 node_modules/foo/dist/node.js,否則會解析為 node_modules/foo/default.js

條件式匯出也可以巢狀在子路徑對應中。

package.json
{
"name": "foo",
"exports": {
"./bar": {
"node": "./dist/bar-node.js",
"default": "./dist/bar-default.js"
}
}
}

這允許匯入 "foo/bar" 以解析為 Node 和其他環境的不同檔案。

條件式匯出也可以互相巢狀,以建立更複雜的邏輯。

package.json
{
"name": "foo",
"exports": {
"node": {
"import": "./dist/node.mjs",
"require": "./dist/node.cjs"
},
"default": "./dist/default.js"
}
}

上述範例定義了模組的不同版本,具體取決於套件是在 Node 環境中透過 ESM import 載入,還是透過 CommonJS require 載入。

Parcel 支援下列匯出條件

匯出條件的解析順序遵循它們在 package.json 中定義的順序,而不是上面列出的順序。

更多範例

#

有關 package.json 匯出的更多詳細資訊,請參閱 Node.js 文件

別名

#

別名可用於覆寫相依項目的正常解析。例如,您可能想要使用不同的但 API 相容的替換來覆寫模組,或將相依項目對應到由從 CDN 載入的函式庫定義的全局變數。

別名是透過 package.json 中的 alias 欄位定義的。它們可以在包含相依項目的來源檔案最近的 package.json 中定義,或在專案根目錄的 package.json 中定義。全局別名套用於專案中的所有檔案和套件,包括 node_modules 中的檔案和套件。

套件別名

#

套件別名將 node_modules 相依項目對應到不同的套件,或對應到專案中的本地檔案。例如,要在專案中的所有檔案和 node_modules 中的任何其他函式庫中將 reactreact-dom 替換為 Preact,您可以在專案根目錄的 package.json 中定義全局別名。

package.json
{
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat"
}
}

您也可以使用從定義別名的 package.json 中的相對路徑,將模組對應到專案中的檔案。

package.json
{
"alias": {
"react": "./my-react.js"
}
}

也支援僅對模組的特定 子路徑 設定別名。此範例會將 lodash/clone 設定別名為 tiny-clonelodash 套件中的其他匯入不受影響。

package.json
{
"alias": {
"lodash/clone": "tiny-clone"
}
}

反之亦然:如果對整個模組設定別名,則該套件的任何子路徑匯入都會在設定別名的模組中解析。例如,如果您將 lodash 設定別名為 my-lodash,並匯入 lodash/clone,這將解析為 my-lodash/clone

檔案別名

#

別名也可以定義為相對路徑,以使用不同的檔案取代套件中的特定檔案。這可以使用 alias 欄位無條件取代檔案,或使用 sourcebrowser 欄位有條件取代檔案。有關這些欄位的詳細資訊,請參閱上方的 套件條目

例如,若要使用特定瀏覽器版本的檔案取代特定檔案,您可以使用 browser 欄位。

package.json
{
"browser": {
"./fs.js": "./fs-browser.js"
}
}

現在,如果在瀏覽器環境中匯入 my-module/fs.js,他們實際上會取得 my-module/fs-browser.js。這適用於外部匯入(例如 套件子路徑),以及模組內的匯入。

Glob 別名

#

檔案別名也可以使用 glob 定義,這允許使用單一模式取代多個檔案。取代可以包含 $1 等模式,以存取擷取的 glob 比對。這可以使用 alias 欄位無條件取代檔案,或使用 sourcebrowser 欄位有條件取代檔案。有關這些欄位的詳細資訊,請參閱上方的 套件條目

例如,您可以使用 source 欄位提供套件中編譯程式碼與原始程式碼之間的對應。當模組是符號連結,或在單一儲存庫中時,這將允許 Parcel 從來源編譯模組,而不是使用預先編譯的版本。

package.json
{
"source": {
"./dist/*": "./src/$1"
}
}

現在,任何時候在 dist 目錄中的檔案被匯入,對應的檔案在 src 資料夾中將會被載入。

Shim 別名

#

檔案或套件可以被別名為 false 以從建置中排除,並用一個空的模組取代。這可以用於從瀏覽器建置中排除某些只在 Node.js 中運作的模組,例如。

package.json
{
"alias": {
"fs": false
}
}

全域別名

#

檔案或套件也可以被別名為全域變數,這些變數將在執行時被定義。例如,一個特定的函式庫可能從 CDN 載入。任何時候對該函式庫的依賴被解析,它將被替換成對該全域變數的參考,而不是被綑綁。

這可以透過建立一個別名到一個具有 global 屬性的物件來完成。以下範例將 jquery 依賴性規格別名為全域變數 $

package.json
{
"alias": {
"jquery": {
"global": "$"
}
}
}

TSConfig

#

Parcel 支援在 TypeScript 的 tsconfig.json 設定檔中定義的一些設定,包括 baseUrlpathsmoduleSuffixes。Parcel 從包含依賴性的檔案向上搜尋,以找到最近的 tsconfig.json 檔案。它也支援使用 extends 選項來合併來自多個 tsconfig 的設定。詳情請參閱 TypeScript 文件

baseUrl

#

baseUrl 欄位定義用於解析 裸規格 的基本目錄。

tsconfig.json
{
"compilerOptions": {
"baseUrl": "./src"
}
}
src/App.js
import 'Home';

在上述範例中,如果 Home 存在,它將解析為 src/Home.js。否則,它將回退到 node_modules/Home,例如。

paths

#

paths 欄位可以用於定義從 裸規格 到檔案路徑的對應。你也可以使用 * 字元定義萬用字元模式。

paths 欄位中引用的檔案路徑相對於 baseUrl(如果已定義),否則相對於包含 tsconfig.json 檔案的目錄。

tsconfig.json
{
"compilerOptions": {
"paths": {
"jquery": ["./vendor/jquery/dist/jquery"],
"app/*": ["./src/app/*"]
}
}
}
src/App.js
import 'jquery';
import 'app/foo';

在上述範例中,jquery 會解析為 ./vendor/jquery/dist/jquery.js,而 app/foo 會解析為 ./src/app/foo.js

moduleSuffixes

#

moduleSuffixes 欄位可讓您指定解析模組時要搜尋的檔案名稱後綴字元。

tsconfig.json
{
"compilerOptions": {
"moduleSuffixes": [".ios", ".native", ""]
}
}
src/App.js
import './foo';

在上述範例中,Parcel 會尋找 ./foo.ios.ts./foo.native.ts./foo.ts(以及其他副檔名,例如 .tsx.js 等)。

設定其他工具

#

本節說明如何設定其他工具,以配合 Parcel 對 Node 解析演算法的擴充功能。

TypeScript

#

TypeScript 需要設定才能支援 Parcel 的功能,例如絕對和波浪符號依存關係指定符號,以及別名。這可以使用 tsconfig.json 中的 paths 選項來完成。請參閱 TypeScript 模組解析文件 以取得更多資訊。

例如,若要將波浪符號路徑對應到根目錄,可以使用下列設定

tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~*": ["./*"]
}
}
}

也可以透過在專案中建立 環境模組 宣告,來支援 URL 架構。例如,若要將使用 bundle-text: 架構載入的依存關係對應到字串,可以使用下列宣告。這可以放置在專案中任何位置的檔案中,例如 parcel.d.ts

parcel.d.ts
declare module 'bundle-text:*' {
const value: string;
export default value;
}

Flow

#

Flow 需要設定才能支援絕對和波浪符號指定符號,以及別名。這可以使用 .flowconfig 中的 module.name_mapper 功能來完成。

例如,若要將絕對指定符號對應到從專案根目錄解析,可以使用下列設定

.flowconfig
[options]
module.name_mapper='^\/\(.*\)$' -> '<PROJECT_ROOT>/\1'

若要啟用 URL 架構,您需要建立對應至 .flow 宣告檔案 的對應,該檔案會匯出預期的類型。例如,若要將使用 bundle-text: 架構載入的相依項對應至字串,您可以建立一個名為 bundle-text.js.flow 的檔案,並將所有參照該架構的相依項對應至該檔案。

bundle-text.js.flow
// @flow
declare var value: string;
export default value;
.flowconfig
[options]
module.name_mapper='^bundle-text:.*$' -> '<PROJECT_ROOT>/bundle-text.js'