JavaScript
Parcel 包含對 JavaScript 的一級支援,包括 ES 模組和 CommonJS,許多類型的相依性、自動轉譯瀏覽器目標、JSX 和 TypeScript 支援,以及更多功能。
模組
#模組允許您將程式碼分隔成不同的檔案,並透過匯入和匯出值來分享它們之間的功能。這有助於您將程式碼結構化成獨立的部分,並為它們之間的溝通定義明確的介面。
Parcel 包含對 ES 模組和 CommonJS 語法的支援。模組指定符會依照 相依性解析 中所述進行解析。
ES 模組
#ES 模組語法是 JavaScript 中匯入和匯出檔案之間值的標準方式。對於新程式碼,應優先於 CommonJS。import
陳述式可用於參考另一個檔案中 export
陳述式所公開的值。
此範例從 math.js
匯入 multiply
函式,並使用它來實作 square
函式。
import {multiply} from './math.js';
export function square(x) {
return multiply(x, x);
}
export function multiply(a, b) {
return a * b;
}
如需深入了解 ES 模組,請參閱 MDN 上的文件。
CommonJS
#CommonJS 是 Node 支援的傳統模組系統,並廣泛用於 npm 上的函式庫。如果您正在撰寫新程式碼,通常應優先使用如上所述的 ES 模組語法。CommonJS 提供一個 require
函式,可用於存取另一個檔案公開的 exports
物件。
此範例使用 require
載入 math.js
模組,並使用其 exports 物件上的 multiply
函式來實作一個 square
函式。
let math = require('./math');
exports.square = function(x) {
return math.multiply(x, x);
};
exports.multiply = function(a, b) {
return a * b;
};
除了 require
和 exports
之外,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'}
選項(請見下方)。如果遺漏這個選項,您會看到類似以下的診斷訊息。
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
這個範例顯示您如何在安裝服務執行緒時預先快取所有套件,並在啟用時清除舊版本。
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 中的相依項時
react
preact
nervjs
hyperapp
正確的 JSX 標記也會根據您使用的程式庫自動推斷。Parcel 也會自動偵測已安裝的 React 或 Preact 版本,並啟用 現代 JSX 轉換(如果支援的話)。
JSX 編譯也可以使用 tsconfig.json
或 jsconfig.json
檔案進行設定。這允許覆寫執行時期、標記和其他選項。請參閱 TSConfig 參考 以取得更多資訊。
{
"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 會相應地轉譯您的程式碼,以確保與您支援的瀏覽器相容。
{
"browserslist": "> 0.5%, last 2 versions, not dead"
}
請參閱 目標 文件,以取得有關如何設定此設定的更多詳細資訊,以及 Parcel 對自動 差異化組合 的支援。
預設情況下,Parcel 支援所有標準 JavaScript 功能,以及已在一個或多個瀏覽器中發布的提案。您也可以使用 tsconfig.json
或 jsconfig.json
檔案啟用對未來提案的支援。目前,唯一支援的提案是 裝飾器,您可能會透過 TypeScript 使用它。
{
"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 處理預設轉換,來改善建置效能。
{
"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 的目標。
{
"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
。
{
"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 也會將模組串接成單一範圍,而不是將每個模組包裝在個別函式中。這稱為「範圍提升」。這有助於讓壓縮更有效率,並透過讓模組之間的參照變成靜態而非動態物件查詢,來提升執行時期效能。
請參閱 範圍提升 文件,以取得讓樹狀搖晃更有效率的秘訣。