相依性解析
當 Parcel 建置您的原始碼時,它會發現相依性,讓程式碼可以拆分為個別檔案,並在多個地方重複使用。相依性會說明如何尋找包含您依賴程式碼的檔案,以及如何建置它的相關資訊。
相依性指定符
#相依性指定符是一個字串,它說明相依性相對於匯入它的檔案的位置。例如,在 JavaScript 中,import
陳述式或 require
函式可用於建立相依性。在 CSS 中,可以使用 @import
和 url()
。通常,這些相依性不會指定完整的絕對路徑,而是由 Parcel 和其他工具解析為絕對路徑的較短指定符。
Parcel 實作了 Node 模組解析演算法 的加強版本。它負責將相依性指定符轉換為可以從檔案系統載入的絕對路徑。除了許多工具支援的標準相依性指定符外,Parcel 還支援一些額外的指定符類型和功能。
相對指定符
#相對指定符以 .
或 ..
開頭,並解析相對於匯入檔案的檔案。
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
。要匯入所有其他檔案類型,則需要檔案副檔名。
以下範例解析為與上面相同的檔案。
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.js
或 index.ts
。上述所有副檔名都支援索引檔案。
import './client';
例如,如果 /path/to/project/src/client
是目錄,則上述規範符可以解析為 /path/to/project/src/client/index.js
。
裸規範符
#裸露的指定符號以任何字元開頭,但下列字元除外:.
、/
、~
或 #
。在 JavaScript、TypeScript 和其他基於 JS 的語言中,它們會解析為 node_modules
中的套件。對於其他類型的檔案,例如 HTML 和 CSS,裸露的指定符號會以與 相對指定符號 相同的方式處理。
import 'react';
在上述範例中,react
可能解析為類似 /path/to/project/node_modules/react/index.js
的內容。確切位置會取決於 node_modules
目錄的位置,以及套件中的組態。
node_modules
目錄會從匯入檔案向上搜尋。搜尋會在專案根目錄停止。例如,如果匯入檔案位於 /path/to/project/src/client/index.js
,則會搜尋下列位置
/path/to/project/src/client/node_modules/react
/path/to/project/src/node_modules/react
/path/to/project/node_modules/react
找到模組目錄後,就會解析套件項目。請參閱 套件項目 以取得有關此程序的更多詳細資訊。
套件子路徑
#裸露的指定符號也可以指定套件內的子路徑。例如,套件可能會發布多個進入點,而不仅仅只發布一個進入點。
import 'lodash/clone';
上述範例會如上所述在 node_modules
目錄中解析 lodash
,然後解析套件中的 clone
模組,而不是其主要進入點。例如,這可能是 node_modules/lodash/clone.js
檔案。
內建模組
#Parcel 包含許多內建 Node.js 模組的 shim,例如 path
和 url
。當依賴項指定符號參照其中一個模組名稱時,會優先使用內建模組,而不是 node_modules
中安裝的具有相同名稱的任何模組。在為節點環境建置時,會從套件中排除內建模組,否則會包含 shim。請參閱 Node 文件 以取得內建模組的完整清單。
在為 Electron 環境建置時,electron
模組也會被視為內建模組,並從套件中排除。
絕對規格
#絕對規格以 /
開頭,並解析相對於專案根目錄的檔案。專案根目錄是專案的基本目錄,通常會包含套件管理員鎖定檔(例如 yarn.lock
或 package-lock.json
),或原始碼控制目錄(例如 .git
)。絕對規格可用於避免在深度巢狀層級中出現非常長的相對路徑。
import '/src/client.js';
上述範例可以放在專案目錄結構中的任何檔案中,任何位置,且永遠會解析為 /path/to/project/src/client.js
。
波浪符號規格
#波浪符號規格以 ~
開頭,並解析相對於匯入檔案中最近的套件根目錄。套件根目錄是一個有 package.json
檔案的目錄,通常會在 node_modules
中找到,或作為單一儲存庫中套件的根目錄。波浪符號規格可用於與絕對規格類似的目的,但當您有多個套件時會更有用。
import '~/src/utils.js';
上述範例會解析為 /path/to/project/packages/frontend/src/utils.js
。
雜湊規格
#雜湊規格以 #
字元開頭,而行為取決於它們所包含的檔案類型。在 JavaScript 和 TypeScript 檔案中,雜湊規格會被視為內部套件匯入,如下所述。在其他檔案中,這些會被視為相對 URL 雜湊。
package.json
中的 "imports"
欄位可用於定義套件中 JavaScript 或 TypeScript 檔案中匯入規格適用的私人對應。這允許套件定義有條件的匯入,具體取決於環境,如下所述 文件 和 Node.js 文件 中所述。
{
"imports": {
"#dep": {
"node": "dep-node",
"browser": "dep-browser"
}
}
}
import '#dep';
查詢參數
#相依性指定符號也可以包含查詢參數,用於指定已解析檔案的轉換選項。例如,您可以在載入影像時指定影像的寬度和高度以調整大小。
.logo {
background: url(logo.png?width=400&height=400);
}
請參閱影像轉換器文件以取得更多關於影像的詳細資訊。您也可以在自訂轉換器外掛程式中使用查詢參數。
注意:查詢參數不支援 CommonJS 指定符號(由 require
函式建立)。
URL 架構
#相依性指定符號可以使用 URL 架構來鎖定命名管線。這些允許您指定與預設管線不同的管線來編譯檔案。例如,bundle-text:
架構可以用於將編譯好的套件內嵌為文字。請參閱套件內嵌以取得更多詳細資訊。
有幾個保留的 URL 架構可能無法用於命名管線,且具有內建行為。
node:
– 指定內建 Node 模組的另一種方式。請參閱內建模組。npm:
– URL 相依性(例如在 HTML、CSS 或網頁工作執行緒中)從node_modules
套件匯入檔案的方式。http:
和https:
– 完全限定的 URL 相依性。這些在執行階段解析,且 Parcel 不會變更。data:
– 資料 URL,包括內嵌的相依性原始碼。目前 Parcel 尚未實作,但保留供未來使用。file:
– 檔案 URL。保留供未來使用。mailto:
和tel:
- 常見的 URL 架構。這些由 Parcel 保留不變。
Glob 指定符號
#Parcel 支援透過 glob 一次匯入多個檔案,然而,由於 glob 匯入是非標準的,因此未包含在預設 Parcel 設定中。若要啟用它們,請將 @parcel/resolver-glob
加入您的 .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 會依序檢查下列欄位
source
– 如果模組位於符號連結之後(例如在單一儲存庫中,或透過npm link
),則 Parcel 會使用source
欄位從來源編譯模組。如果套件有多個進入點,source
欄位也可以用作別名對應,詳情請參閱下方的 別名。exports
– 套件匯出,詳情請參閱下方。browser
– 套件的特定瀏覽器版本。如果要建置 瀏覽器環境,browser
欄位會覆寫其他欄位。如果套件有多個進入點,browser
欄位也可以用作別名對應,詳情請參閱下方的 別名。module
– 套件的 ES 模組版本。main
– 套件的 CommonJS 版本。
如果未設定這些欄位,或這些欄位指向的檔案不存在,解析就會回退到索引檔案。詳情請參閱 目錄索引檔案。
套件匯出
#package.json
中的 "exports"
欄位可用於定義套件的公開存取進入點。這些欄位也可以根據環境定義條件式行為,允許解析根據模組匯入來源位置(例如節點或瀏覽器)而變更。
啟用套件匯出
#套件匯出預設為停用,因為它們可能會中斷未考量到它們的現有專案。您可以在專案根目錄的 package.json
檔案中新增下列內容來啟用支援。
{
"@parcel/resolver-default": {
"packageExports": true
}
}
單一匯出
#如果套件只有一個匯出的模組,"exports"
欄位可以定義為字串
{
"name": "foo",
"exports": "./dist/index.js"
}
當使用者在上述範例中匯入 "foo"
套件時,將會解析 node_modules/foo/dist/index.js
模組。
多重匯出
#如果套件匯出多個模組,"exports"
欄位可以提供對應,定義如何尋找這些匯出。"."
匯出定義主要進入點,其他進入點則定義為子路徑。
{
"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"
會導致建置時間錯誤。
*
字元可用於匯出對應中的萬用字元。對應的左方只會出現一個 *
,而對應的字串會取代右方中的每個執行個體。這可讓您避免手動列出每個您要匯出的檔案。
{
"name": "foo",
"exports": {
"./*": "./dist/*.js"
}
}
在上述範例中,dist
目錄中的所有 .js
檔案都會從套件中匯出,不含副檔名。例如,匯入 "foo/bar"
會解析為 node_modules/foo/dist/bar.js
。
條件式匯出
#"exports"
欄位也可以在不同的 環境 或其他條件中,為相同的指定符號定義不同的對應。例如,套件可以為 ES 模組和 CommonJS,或 Node 和瀏覽器提供不同的進入點。
{
"name": "foo",
"exports": {
"node": "./dist/node.js",
"default": "./dist/default.js"
}
}
在上述範例中,如果消費者從 Node 環境匯入 "foo"
模組,它會解析為 node_modules/foo/dist/node.js
,否則會解析為 node_modules/foo/default.js
。
條件式匯出也可以巢狀在子路徑對應中。
{
"name": "foo",
"exports": {
"./bar": {
"node": "./dist/bar-node.js",
"default": "./dist/bar-default.js"
}
}
}
這允許匯入 "foo/bar"
以解析為 Node 和其他環境的不同檔案。
條件式匯出也可以互相巢狀,以建立更複雜的邏輯。
{
"name": "foo",
"exports": {
"node": {
"import": "./dist/node.mjs",
"require": "./dist/node.cjs"
},
"default": "./dist/default.js"
}
}
上述範例定義了模組的不同版本,具體取決於套件是在 Node 環境中透過 ESM import
載入,還是透過 CommonJS require
載入。
Parcel 支援下列匯出條件
"import"
– 套件是使用 ESMimport
語法引用的。"require"
– 套件是使用 CommonJSrequire
函式引用的。"module"
– 套件是由 ESMimport
語法或 CommonJSrequire
函式參照的。"sass"
– 套件是由 Sass 樣式表參照的。"less"
– 套件是由 Less 樣式表參照的。"stylus"
– 套件是由 Stylus 樣式表參照的。"style"
– 套件是由樣式表參照的 (例如 CSS、Sass、Stylus 等)。"node"
– 輸出會在 Node 環境中執行。"browser"
– 輸出會在瀏覽器環境中執行。"worker"
– 輸出會在網頁工作執行緒或服務工作執行緒環境中執行。"worklet"
– 輸出會在工作執行緒環境中執行。"electron"
– 輸出會在 Electron 環境中執行。"development"
– 套件是在開發組建中載入的。"production"
– 套件是在生產組建中載入的。"default"
– 在沒有其他條件符合時,預設的條件。
匯出條件的解析順序遵循它們在 package.json 中定義的順序,而不是上面列出的順序。
更多範例
#有關 package.json 匯出的更多詳細資訊,請參閱 Node.js 文件。
別名
#別名可用於覆寫相依項目的正常解析。例如,您可能想要使用不同的但 API 相容的替換來覆寫模組,或將相依項目對應到由從 CDN 載入的函式庫定義的全局變數。
別名是透過 package.json 中的 alias
欄位定義的。它們可以在包含相依項目的來源檔案最近的 package.json
中定義,或在專案根目錄的 package.json
中定義。全局別名套用於專案中的所有檔案和套件,包括 node_modules
中的檔案和套件。
套件別名
#套件別名將 node_modules
相依項目對應到不同的套件,或對應到專案中的本地檔案。例如,要在專案中的所有檔案和 node_modules
中的任何其他函式庫中將 react
和 react-dom
替換為 Preact,您可以在專案根目錄的 package.json
中定義全局別名。
{
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat"
}
}
您也可以使用從定義別名的 package.json
中的相對路徑,將模組對應到專案中的檔案。
{
"alias": {
"react": "./my-react.js"
}
}
也支援僅對模組的特定 子路徑 設定別名。此範例會將 lodash/clone
設定別名為 tiny-clone
。lodash
套件中的其他匯入不受影響。
{
"alias": {
"lodash/clone": "tiny-clone"
}
}
反之亦然:如果對整個模組設定別名,則該套件的任何子路徑匯入都會在設定別名的模組中解析。例如,如果您將 lodash
設定別名為 my-lodash
,並匯入 lodash/clone
,這將解析為 my-lodash/clone
。
檔案別名
#別名也可以定義為相對路徑,以使用不同的檔案取代套件中的特定檔案。這可以使用 alias
欄位無條件取代檔案,或使用 source
或 browser
欄位有條件取代檔案。有關這些欄位的詳細資訊,請參閱上方的 套件條目。
例如,若要使用特定瀏覽器版本的檔案取代特定檔案,您可以使用 browser
欄位。
{
"browser": {
"./fs.js": "./fs-browser.js"
}
}
現在,如果在瀏覽器環境中匯入 my-module/fs.js
,他們實際上會取得 my-module/fs-browser.js
。這適用於外部匯入(例如 套件子路徑),以及模組內的匯入。
Glob 別名
#檔案別名也可以使用 glob 定義,這允許使用單一模式取代多個檔案。取代可以包含 $1
等模式,以存取擷取的 glob 比對。這可以使用 alias
欄位無條件取代檔案,或使用 source
或 browser
欄位有條件取代檔案。有關這些欄位的詳細資訊,請參閱上方的 套件條目。
例如,您可以使用 source
欄位提供套件中編譯程式碼與原始程式碼之間的對應。當模組是符號連結,或在單一儲存庫中時,這將允許 Parcel 從來源編譯模組,而不是使用預先編譯的版本。
{
"source": {
"./dist/*": "./src/$1"
}
}
現在,任何時候在 dist
目錄中的檔案被匯入,對應的檔案在 src
資料夾中將會被載入。
Shim 別名
#檔案或套件可以被別名為 false
以從建置中排除,並用一個空的模組取代。這可以用於從瀏覽器建置中排除某些只在 Node.js 中運作的模組,例如。
{
"alias": {
"fs": false
}
}
全域別名
#檔案或套件也可以被別名為全域變數,這些變數將在執行時被定義。例如,一個特定的函式庫可能從 CDN 載入。任何時候對該函式庫的依賴被解析,它將被替換成對該全域變數的參考,而不是被綑綁。
這可以透過建立一個別名到一個具有 global
屬性的物件來完成。以下範例將 jquery
依賴性規格別名為全域變數 $
。
{
"alias": {
"jquery": {
"global": "$"
}
}
}
TSConfig
#Parcel 支援在 TypeScript 的 tsconfig.json
設定檔中定義的一些設定,包括 baseUrl
、paths
和 moduleSuffixes
。Parcel 從包含依賴性的檔案向上搜尋,以找到最近的 tsconfig.json
檔案。它也支援使用 extends
選項來合併來自多個 tsconfig 的設定。詳情請參閱 TypeScript 文件。
baseUrl
#baseUrl
欄位定義用於解析 裸規格 的基本目錄。
{
"compilerOptions": {
"baseUrl": "./src"
}
}
import 'Home';
在上述範例中,如果 Home
存在,它將解析為 src/Home.js
。否則,它將回退到 node_modules/Home
,例如。
paths
#paths
欄位可以用於定義從 裸規格 到檔案路徑的對應。你也可以使用 *
字元定義萬用字元模式。
在 paths
欄位中引用的檔案路徑相對於 baseUrl
(如果已定義),否則相對於包含 tsconfig.json
檔案的目錄。
{
"compilerOptions": {
"paths": {
"jquery": ["./vendor/jquery/dist/jquery"],
"app/*": ["./src/app/*"]
}
}
}
import 'jquery';
import 'app/foo';
在上述範例中,jquery
會解析為 ./vendor/jquery/dist/jquery.js
,而 app/foo
會解析為 ./src/app/foo.js
。
moduleSuffixes
#moduleSuffixes
欄位可讓您指定解析模組時要搜尋的檔案名稱後綴字元。
{
"compilerOptions": {
"moduleSuffixes": [".ios", ".native", ""]
}
}
import './foo';
在上述範例中,Parcel 會尋找 ./foo.ios.ts
、./foo.native.ts
和 ./foo.ts
(以及其他副檔名,例如 .tsx
、.js
等)。
設定其他工具
#本節說明如何設定其他工具,以配合 Parcel 對 Node 解析演算法的擴充功能。
TypeScript
#TypeScript 需要設定才能支援 Parcel 的功能,例如絕對和波浪符號依存關係指定符號,以及別名。這可以使用 tsconfig.json
中的 paths
選項來完成。請參閱 TypeScript 模組解析文件 以取得更多資訊。
例如,若要將波浪符號路徑對應到根目錄,可以使用下列設定
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~*": ["./*"]
}
}
}
也可以透過在專案中建立 環境模組 宣告,來支援 URL 架構。例如,若要將使用 bundle-text:
架構載入的依存關係對應到字串,可以使用下列宣告。這可以放置在專案中任何位置的檔案中,例如 parcel.d.ts
。
declare module 'bundle-text:*' {
const value: string;
export default value;
}
Flow
#Flow 需要設定才能支援絕對和波浪符號指定符號,以及別名。這可以使用 .flowconfig
中的 module.name_mapper 功能來完成。
例如,若要將絕對指定符號對應到從專案根目錄解析,可以使用下列設定
[options]
module.name_mapper='^\/\(.*\)$' -> '<PROJECT_ROOT>/\1'
若要啟用 URL 架構,您需要建立對應至 .flow
宣告檔案 的對應,該檔案會匯出預期的類型。例如,若要將使用 bundle-text:
架構載入的相依項對應至字串,您可以建立一個名為 bundle-text.js.flow
的檔案,並將所有參照該架構的相依項對應至該檔案。
// @flow
declare var value: string;
export default value;
[options]
module.name_mapper='^bundle-text:.*$' -> '<PROJECT_ROOT>/bundle-text.js'