程式碼拆分

Parcel 支援開箱即用的零組態程式碼分割。這讓你可以將應用程式程式碼分割成獨立的套件,這些套件可以在需要時載入,進而縮小初始套件大小並加快載入時間。

程式碼分割由動態 import() 語法控制,其運作方式與一般 import 陳述式類似,但會傳回 Promise。這表示模組可以非同步載入。

使用動態載入

#

以下範例說明如何使用動態載入在需要時載入應用程式的子頁面。

pages/index.js
import("./pages/about").then(function (page) {
// Render page
page.render();
});
pages/about.js
export function render() {
// Render the page
}

由於 import() 會傳回 Promise,因此你也可以使用 async/await 語法。

pages/index.js
async function load() {
const page = await import("./pages/about");
// Render page
page.render();
}
load();
pages/about.js
export function render() {
// Render the page
}

樹狀搖晃

#

當 Parcel 可以判斷你使用動態載入模組的哪些匯出時,它會從該模組中樹狀搖晃未使用的匯出。這適用於靜態屬性存取或解構,並搭配 await 或 Promise .then 語法。

注意:對於 await 的情況,很遺憾地,只有在未轉譯掉 await 時才能移除未使用的匯出(例如,使用現代的 browserslist 組態)。

let { x: y } = await import("./b.js");
let ns = await import("./b.js");
console.log(ns.x);
import("./b.js").then((ns) => console.log(ns.x));
import("./b.js").then(({ x: y }) => console.log(y));

共用套件

#

當應用程式的多個部分依賴於相同的共用模組時,這些模組會自動去重並封裝成獨立的套件。這讓常用的依賴項可以與應用程式程式碼並行載入,並由瀏覽器個別快取。

例如,如果您的應用程式有多個頁面,且這些頁面包含依賴於相同共用模組的 <script> 標籤,這些模組會拆分到「共用套件」中。如此一來,如果使用者從一個頁面導覽到另一個頁面,他們只需要下載該頁面的新程式碼,而不用下載頁面之間的共用相依性。

home.html
<!doctype html>
<div id="app"></div>
<script type="module" src="home.js"></script>
home.js
import { createRoot } from 'react-dom';

createRoot(app).render(<h1>Home</h1>, app);
profile.html
<!doctype html>
<div id="app"></div>
<script type="module" src="profile.js"></script>
profile.js
import { createRoot } from 'react-dom';

createRoot(app).render(<h1>Profile</h1>, app);

已編譯的 HTML

home.html
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="home.fac9ed.js"></script>
profile.html
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="profile.9fc67e.js"></script>

在上述範例中,home.jsprofile.js 都依賴於 react-dom,因此會將其拆分到一個獨立的套件中,並透過在兩個 HTML 頁面中加入額外的 <script> 標籤,讓它們並行載入。

這也適用於應用程式中已使用動態 import() 進行程式碼拆分的不同區段。兩個動態匯入之間共用的相依性會拆分出來,並與動態匯入的模組並行載入。

組態

#

預設情況下,Parcel 只有在共用模組達到一定大小門檻時才會建立共用套件。這樣可以避免拆分非常小的模組,以及產生額外的 HTTP 要求,即使使用 HTTP/2,這些要求也會造成負擔。如果模組小於門檻值,它會在各個頁面之間重複,而不是拆分。

Parcel 也有最大並行要求限制,以避免一次向瀏覽器發出太多要求而造成過載,如果達到此限制,它會重複模組。在建立共用套件時,會優先處理較大的模組,而非較小的模組。

預設情況下,這些參數已針對 HTTP/2 調整。但是,您可以調整這些選項,以提高或降低應用程式的參數。您可以透過設定專案根目錄中 package.json 中的 @parcel/bundler-default 鍵來執行此操作。

package.json
{
"@parcel/bundler-default": {
"minBundles": 1,
"minBundleSize": 3000,
"maxParallelRequests": 20
}
}

可用的選項如下

http minBundles minBundleSize maxParallelRequests
1 1 30000 6
2 (預設) 1 20000 25

您可以在 web.dev 上閱讀更多關於此主題的資訊。

內部非同步套件

#

如果模組在同一個套件中同步和非同步匯入,而非將它拆分到一個獨立的套件,則非同步相依性將會「內部化」。這表示它會和動態匯入保留在同一個套件中以避免重複,但會包裝在 Promise 中以保留語意。

因此,動態匯入只是一個相依性不需要同步的提示,並非保證會建立新的套件。

重複資料刪除

#

如果動態匯入的模組具有在所有可能的祖先中都已經存在的相依性,則會刪除重複資料。例如,如果一個頁面匯入一個也由動態匯入的模組使用的函式庫,則該函式庫只會包含在父項中,因為它在執行階段已經在頁面上。

手動共用套件

#

注意:手動共用套件目前為實驗性質,可能會變更。

預設情況下,Parcel 會自動將常用的模組拆分為「共用套件」,並在上述情況中建立套件。不過,在某些情況下,您可能想要明確指定套件中包含哪些內容,以及誰可以要求該套件。

這些情況包括但不限於...

手動共用套件可以在專案根目錄的 package.json 中指定。assets 屬性必須設定為 glob 清單。任何與這些 glob 相符的資產檔案路徑都將包含在手動共用套件中。

此範例建立一個供應商套件,其中包含從 manual.js 開始的圖表中所有 JS 資產,分成 3 個並行的 HTTP 要求。

package.json
{
"@parcel/bundler-default": {
"manualSharedBundles": [
{
"name": "vendor",
"root": "manual.js",
"assets": ["**/*"],
"types": ["js"],
"split": 3
},
],
},
}

完整的選項清單如下

小心!

設定手動共用套件會覆寫 Parcel 通常執行的所有自動程式碼拆分,並可能導致意外的載入順序問題,因為它會對你的整個程式碼庫進行對應,包括 node_modules。請注意你使用的 glob,每個檔案類型只指定 1 個套件,我們建議你指定一個根目錄檔案。