JavaScript

Parcel 包含對 JavaScript 的一級支援,包括 ES 模組和 CommonJS,許多類型的相依性、自動轉譯瀏覽器目標、JSX 和 TypeScript 支援,以及更多功能。

模組

#

模組允許您將程式碼分隔成不同的檔案,並透過匯入和匯出值來分享它們之間的功能。這有助於您將程式碼結構化成獨立的部分,並為它們之間的溝通定義明確的介面。

Parcel 包含對 ES 模組和 CommonJS 語法的支援。模組指定符會依照 相依性解析 中所述進行解析。

ES 模組

#

ES 模組語法是 JavaScript 中匯入和匯出檔案之間值的標準方式。對於新程式碼,應優先於 CommonJS。import 陳述式可用於參考另一個檔案中 export 陳述式所公開的值。

此範例從 math.js 匯入 multiply 函式,並使用它來實作 square 函式。

square.js
import {multiply} from './math.js';

export function square(x) {
return multiply(x, x);
}
math.js
export function multiply(a, b) {
return a * b;
}

如需深入了解 ES 模組,請參閱 MDN 上的文件。

CommonJS

#

CommonJS 是 Node 支援的傳統模組系統,並廣泛用於 npm 上的函式庫。如果您正在撰寫新程式碼,通常應優先使用如上所述的 ES 模組語法。CommonJS 提供一個 require 函式,可用於存取另一個檔案公開的 exports 物件。

此範例使用 require 載入 math.js 模組,並使用其 exports 物件上的 multiply 函式來實作一個 square 函式。

square.js
let math = require('./math');

exports.square = function(x) {
return math.multiply(x, x);
};
math.js
exports.multiply = function(a, b) {
return a * b;
};

除了 requireexports 之外,Parcel 也支援 module.exports,以及 __dirname__filename 模組全域變數,以及 process.env 以存取環境變數。請參閱 Node 模擬 文件以取得更多詳細資訊。

如需深入了解 CommonJS,請參閱 Node.js 文件

動態匯入

#

ES 模組 import 陳述式和 CommonJS require 函式都會同步載入相依項:也就是說,可以在不等待網路要求的情況下立即參照模組。通常,同步匯入的模組會與其父項打包到同一個檔案中,或在已載入的另一個套件中參照。

動態 import() 函式可用於非同步載入相依項。這允許依需求延遲載入程式碼,並且是減少應用程式初始頁面載入檔案大小的良好技術。import() 會傳回一個 Promise,當相依項載入時會通知您。

import('./pages/about').then(function(page) {
page.render()
});

請參閱 程式碼拆分 文件以取得有關如何使用動態匯入的更多詳細資訊。

傳統腳本

#

模組(ES 模組和 CommonJS)的其中一個好處是它們會隔離功能。這表示在頂層範圍宣告的變數無法在模組外存取,除非它們已匯出。這通常是一個很棒的特徵,但 JavaScript 中的模組相對較新,而且有許多舊版程式庫和腳本不希望被隔離。

傳統腳本是一個 JavaScript 檔案,透過 HTML 中的傳統 <script> 標籤(沒有 type="module")或其他方式(例如 Workers)載入。傳統腳本將頂層範圍中的變數視為全域變數,即使在不同的腳本之間也可以存取。

例如,在載入像 jQuery 這樣的程式庫時,$ 變數會在頁面上的其他腳本中全域可用。如果 jQuery 是以模組載入,$ 將無法存取,除非手動將它指定為 window 物件的屬性。

<script src="jquery.js"></script>
<script>
// The $ variable is now available globally.
$('.banner').show();
</script>

此外,傳統腳本不支援透過 ES 模組或 CommonJS 進行同步匯入或匯出,而且 Node 模擬 已停用。不過,動態 import() 確實 支援從傳統腳本中載入模組。

Parcel 比對傳統腳本和模組的瀏覽器行為。如果您希望在程式碼中使用匯入或匯出,您需要使用 <script type="module"> 從 HTML 檔案參照您的 JavaScript。對於工作執行緒,請使用 {type: 'module'} 選項(請見下方)。如果遺漏這個選項,您會看到類似以下的診斷訊息。

Screenshot of an error message showing "Browser scripts cannot have imports or exports. Add the type='module' attribute to the script tag."

import.meta

#

import.meta 物件包含其被引用的模組資訊。Parcel 目前支援單一屬性 import.meta.url,其中包含檔案系統上模組的 file:// url。此 URL 相對於您的專案根目錄(例如,Git 初始化的目錄),以避免公開任何建置系統的詳細資訊。

console.log(import.meta);
// => {url: 'file:///src/App.js'}

console.log(import.meta.url);
// => 'file:///src/App.js'

URL 相依性

#

您可以使用 URL 建構函式,從 JavaScript 檔案中參照非 JavaScript 資產,例如圖片或影片。這些資產會使用 import.meta.url 作為基本 URL 參數,相對於模組解析。第一個引數必須是字串文字才能被辨識(不是變數或表達式)。

此範例參照與 JavaScript 檔案位於同一個目錄中的圖片 hero.jpg,並由此圖片建立一個 <img> 元素。

let img = document.createElement('img');
img.src = new URL('hero.jpg', import.meta.url);
document.body.appendChild(img);

Parcel 會將 URL 相依性所參照的任何檔案視為其他相依性一樣處理。例如,圖片會由圖片轉換器處理,而您可以使用 查詢參數 來指定選項以調整大小並轉換圖片。如果沒有為特定檔案類型設定轉換器,則檔案將會複製到 dist 目錄中,且不修改。產生的檔案名稱也會包含 內容雜湊,以在瀏覽器中長期快取。

工作執行緒

#

Parcel 內建支援網路工作執行緒、服務工作執行緒和工作函式,這些功能允許將工作移到不同的執行緒。

網路工作執行緒

#

網路工作執行緒是最通用的工作執行緒類型。它們允許您在背景執行緒中執行任意 CPU 密集的工作,以避免阻擋使用者介面。工作執行緒是使用 Worker 建構函式建立,並使用上述的 URL 建構函式參照另一個 JavaScript 檔案。

要在工作執行緒中使用 ES 模組或 CommonJS 語法,請使用上述 傳統腳本 中所述的 type: 'module' 選項。Parcel 會根據您的 目標 和目前的瀏覽器支援,在必要時將您的工作執行緒編譯為非模組工作執行緒。

new Worker(
new URL('worker.js', import.meta.url),
{type: 'module'}
);

Parcel 也支援 SharedWorker 建構函式,它會建立一個可以在不同的瀏覽器視窗或 iframe 中存取的執行緒。它支援與上面為 Worker 說明的相同選項。

若要深入了解網路執行緒,請參閱 MDN 上的文件。

服務執行緒

#

服務執行緒在背景執行,並提供離線快取、背景同步和推播通知等功能。它們是使用 navigator.serviceWorker.register 函式建立,並使用 URL 建構函式來參照另一個 JavaScript 檔案。

若要在服務執行緒中使用 ES 模組或 CommonJS 語法,請使用上面 傳統腳本 中說明的 type: 'module' 選項。Parcel 會根據您的 目標 和目前的瀏覽器支援,在必要時將您的服務執行緒編譯為非模組執行緒。

navigator.serviceWorker.register(
new URL('service-worker.js', import.meta.url),
{type: 'module'}
);

注意:服務執行緒不支援動態 import()

服務執行緒通常用於預先快取靜態資產,例如 JavaScript、CSS 和圖片。@parcel/service-worker 可用於從您的服務執行緒中存取一組套件 URL。它還提供一個 version hash,它會在每次清單變更時變更。您可以使用它來產生快取名稱。

首先,將它安裝為專案中的相依項。

yarn add @parcel/service-worker

這個範例顯示您如何在安裝服務執行緒時預先快取所有套件,並在啟用時清除舊版本。

service-worker.js
import {manifest, version} from '@parcel/service-worker';

async function install() {
const cache = await caches.open(version);
await cache.addAll(manifest);
}
addEventListener('install', e => e.waitUntil(install()));

async function activate() {
const keys = await caches.keys();
await Promise.all(
keys.map(key => key !== version && caches.delete(key))
);
}
addEventListener('activate', e => e.waitUntil(activate()));

若要深入了解服務執行緒,請參閱 MDN 上的文件,以及 web.dev 上的 離線食譜

傳統腳本執行緒

#

type: 'module' 選項也可以省略,以將工作執行緒和服務工作執行緒視為傳統腳本,而不是模組。importScripts 函數可用於在傳統腳本工作執行緒中載入額外的程式碼,但是,程式碼會在執行時期載入,不會由 Parcel 處理。這是因為 importScripts 會解析相對於頁面的 URL,而不是工作執行緒腳本。因此,importScripts 的參數必須是完全限定的絕對 URL,而不是相對路徑。

以下範例顯示受支援和不受支援的模式。

// ✅ absolute URL
importScripts('http://some-cdn.com/worker.js');

// ✅ computed URL
importScripts(location.origin + '/worker.js');

// 🚫 relative path
importScripts('worker.js');

// 🚫 absolute path
importScripts('/worker.js');

工作執行緒

#

Parcel 也支援工作執行緒,包括 CSS Houdini 繪製工作執行緒 以及 Web 音訊工作執行緒。這些讓您在瀏覽器中連接到渲染程序或音訊處理管線的低階層面。

繪製工作執行緒會使用下列語法自動偵測

CSS.paintWorklet.addModule(
new URL('worklet.js', import.meta.url)
);

Web 音訊工作執行緒無法靜態分析,因此對於這些工作執行緒,您可以使用 worklet: 架構來匯入針對正確環境編譯的工作執行緒檔案的 URL。

import workletUrl from 'worklet:./worklet.js';

context.audioWorklet.addModule(workletUrl);

工作執行緒永遠都是模組,沒有傳統腳本工作執行緒。這表示 type: 'module' 選項對工作執行緒來說不是必要的,而且不支援 importScripts

此外,工作執行緒中不支援動態 import()

轉譯

#

Parcel 內建支援 JSX、TypeScript 和 Flow 的轉譯,以及將現代 JavaScript 語法轉譯為支援舊瀏覽器的功能。此外,還支援 Babel 來啟用實驗性或自訂的 JavaScript 轉換。

JSX

#

Parcel 內建支援 JSX。JSX 會在 .jsx.tsx 檔案中自動啟用,或者當下列其中一個函式庫定義為 package.json 中的相依項時

正確的 JSX 標記也會根據您使用的程式庫自動推斷。Parcel 也會自動偵測已安裝的 React 或 Preact 版本,並啟用 現代 JSX 轉換(如果支援的話)。

JSX 編譯也可以使用 tsconfig.jsonjsconfig.json 檔案進行設定。這允許覆寫執行時期、標記和其他選項。請參閱 TSConfig 參考 以取得更多資訊。

tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}

Flow

#

flow-bin 列在專案根目錄 package.json 的依賴項中時,Flow 支援會自動啟用。您也必須在您希望編譯的檔案中使用 @flow 指令。

Parcel 目前使用 Babel 來移除 Flow 型別。如果您有自訂 Babel 設定檔,您需要自行新增 Flow 外掛程式。請參閱 Babel 以取得更多詳細資訊。

TypeScript

#

請參閱 TypeScript

瀏覽器相容性

#

預設情況下,Parcel 沒有針對舊瀏覽器執行任何 JavaScript 語法轉譯。這表示如果您使用現代語言功能撰寫程式碼,Parcel 會輸出這些功能。您可以使用 package.json 中的 browserslist 欄位宣告您的應用程式支援的瀏覽器。當宣告此欄位時,Parcel 會相應地轉譯您的程式碼,以確保與您支援的瀏覽器相容。

package.json
{
"browserslist": "> 0.5%, last 2 versions, not dead"
}

請參閱 目標 文件,以取得有關如何設定此設定的更多詳細資訊,以及 Parcel 對自動 差異化組合 的支援。

預設情況下,Parcel 支援所有標準 JavaScript 功能,以及已在一個或多個瀏覽器中發布的提案。您也可以使用 tsconfig.jsonjsconfig.json 檔案啟用對未來提案的支援。目前,唯一支援的提案是 裝飾器,您可能會透過 TypeScript 使用它。

tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
}
}

更多實驗性功能可以使用 Babel 啟用。

Babel

#

Babel 是 JavaScript 的熱門轉譯器,擁有龐大的外掛生態系統。將 Babel 與 Parcel 搭配使用的方式與單獨使用或與其他建置工具搭配使用相同。建立一個 Babel 設定檔,例如 .babelrc,Parcel 會自動選取它。

Parcel 支援專案範圍設定檔,例如 babel.config.json,以及檔案相對設定檔,例如 .babelrc。請參閱 Babel 文件,以取得更多設定檔詳細資料。

注意:應避免使用 JavaScript Babel 設定檔(例如 babel.config.js)。這些設定檔會導致 Parcel 的快取效能降低,表示每次重新啟動 Parcel 時,所有 JS 檔案都會重新編譯。為避免此情況,請改用基於 JSON 的設定檔格式(例如 babel.config.json)。

預設預設值

#

Parcel 預設包含針對瀏覽器目標(等同於 @babel/preset-env)、JSX(等同於 @babel/preset-react)、TypeScript(等同於 @babel/preset-typescript)和 Flow(等同於 @babel/preset-flow)的轉譯。如果這些是你專案中唯一需要的轉換,那麼你可能根本不需要 Babel。

如果你有一個現有的專案,其 Babel 設定檔僅包含上述預設值,你可能會將其移除。這可以顯著改善建置效能,因為 Parcel 的內建轉譯器比 Babel 快很多。

自訂外掛

#

如果你有上述以外的自訂 Babel 預設值或外掛,你可以設定 Babel 只包含這些自訂外掛,並略過標準預設值。這會透過讓 Babel 執行較少工作,並讓 Parcel 處理預設轉換,來改善建置效能。

babel.config.json
{
"plugins": ["@emotion/babel-plugin"]
}

由於 Parcel 使用 Babel 來轉譯 Flow,因此您需要在 Babel 設定檔中保留 @babel/preset-flow,以及任何自訂外掛程式。否則,您的 Babel 設定檔會覆寫 Parcel 的預設值。上面列出的其他 Babel 預設值可以移除。

@babel/preset-env@babel/plugin-transform-runtime 不會辨識 Parcel 的 目標,這表示 差異化套件 無法正常運作。這可能會導致不必要的轉譯和產生較大的套件大小。此外,@babel/preset-env 預設會轉譯 ES 模組,這可能會導致 範圍提升 的問題。

@babel/preset-env@babel/plugin-transform-runtime 並非必要,因為 Parcel 會自動處理瀏覽器目標的轉譯。不過,如果您基於某些原因需要這些外掛程式,您可以使用 Parcel 的包裝函式,它們會辨識 Parcel 的目標。

babel.config.json
{
"presets": ["@parcel/babel-preset-env"],
"plugins": ["@parcel/babel-plugin-transform-runtime"]
}

與其他工具搭配使用

#

雖然 Parcel 預設包含轉譯功能,您可能仍需要將 Babel 與其他工具搭配使用,例如測試執行器,如 Jest,以及 linter,如 ESLint。如果是這種情況,您可能無法完全移除 Babel 設定檔。您可以讓 Parcel 忽略您的 Babel 設定檔,這將帶來效能優勢,並防止上述其他問題。

若要在 Parcel 中停用 Babel 轉譯,請覆寫 JavaScript 的預設 Parcel 設定檔,以排除 @parcel/transformer-babel

.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{js,mjs,jsx,cjs,ts,tsx}": [
"@parcel/transformer-js",
"@parcel/transformer-react-refresh-wrap"
]
}
}

這將允許其他工具繼續使用您的 Babel 設定檔,但在 Parcel 中停用 Babel 轉譯。

生產

#

在生產模式中,Parcel 包含最佳化功能,以減少程式碼檔案大小。請參閱 生產,以取得有關其運作方式的更多詳細資料。

縮小

#

在生產模式中,Parcel 會自動壓縮您的程式碼以減少您的套件檔案大小。預設情況下,Parcel 使用 SWC 來執行壓縮。為了向後相容性,這支援 Terser 設定檔。若要設定壓縮器,您可以在專案根目錄中建立一個 .terserrc 檔案。請參閱 SWC 文件 以取得有關可用選項的資訊。

Parcel 的先前版本使用 Terser 作為預設 JavaScript 壓縮器。若要繼續使用 Terser 而不是 SWC,您可以在 .parcelrc 中設定 Parcel 使用 @parcel/optimizer-terser 外掛程式。請參閱 外掛程式 以取得更多資訊。

樹狀搖晃

#

在生產版本中,Parcel 會靜態分析每個模組的匯入和匯出,並移除所有未使用的內容。這稱為「樹狀搖晃」或「死碼消除」。樹狀搖晃支援靜態和動態 import()、CommonJS 和 ES 模組,甚至跨語言支援 CSS 模組。

在可能的情況下,Parcel 也會將模組串接成單一範圍,而不是將每個模組包裝在個別函式中。這稱為「範圍提升」。這有助於讓壓縮更有效率,並透過讓模組之間的參照變成靜態而非動態物件查詢,來提升執行時期效能。

請參閱 範圍提升 文件,以取得讓樹狀搖晃更有效率的秘訣。