遷移

絕大部分來說,Parcel 2 的運作方式與 Parcel 1 十分類似,但升級時有幾件事需要變更。

入門

#

讓我們逐步了解從 Parcel 1 升級到 Parcel 2 的幾個基本步驟。

套件名稱

#

從 Parcel 1 升級到 Parcel 2 時,首先要注意的是 npm 套件名稱已從 parcel-bundler 變更為 parcel。您需要相應地更新 package.json 中的相依性。

package.json:
{
"devDependencies": {
"parcel-bundler": "^1.12.5"
}
}
package.json:
{
"devDependencies": {
"parcel": "^2.0.0"
}
}

您也可以使用套件管理員來執行此動作,例如 npmyarn

yarn remove parcel-bundler
yarn add parcel --dev

快取位置

#

Parcel 快取的預設位置也已從 .cache 變更為 .parcel-cache。您需要修改 .gitignore 或類似設定來因應此變更

.gitignore:
.cache
.gitignore:
.parcel-cache

程式碼變更

#

<script type="module">

#

在 Parcel 1 中,從 HTML 檔案中的 <script> 標籤引用的 JavaScript 檔案被視為模組,支援 ES 模組和 CommonJS 語法來匯入和匯出值。然而,這與瀏覽器的實際運作方式不符,其中「傳統腳本」不支援匯入和匯出,且頂層變數被視為全域變數。

Parcel 2 與瀏覽器行為相符:傳統 <script> 標籤不支援匯入或匯出。請使用 <script type="module"> 元素來引發模組。這也會自動產生 nomodule 版本,適用於較舊的瀏覽器,具體取決於您的 browserslist。請參閱 差異化打包 以取得詳細資訊。

index.html:
<!doctype html>
<html>
<head>
<script src="app.js"></script>
</head>
</html>
index.html:
<!doctype html>
<html>
<head>
<script type="module" src="app.js"></script>
</head>
</html>

注意:新增 type="module" 屬性也會影響腳本的載入行為。傳統腳本是「阻擋渲染」,表示在腳本執行完成之前,不會解析 HTML 文件的其餘部分。模組腳本不會阻擋渲染,而且會延遲執行,直到 HTML 完全解析完畢。Parcel 會自動插入 defer 屬性,以符合舊瀏覽器和開發模式中的此行為。這表示 document.write 等功能無法在模組腳本中運作。如果您依賴這些功能,請改用現代 API 或繼續使用傳統腳本處理應用程式的那個部分。請參閱 MDN 上的文件,以進一步了解模組和傳統腳本之間的差異。

請參閱 傳統腳本,以進一步了解傳統腳本與模組腳本的差異。

從 JavaScript 匯入非程式碼資源

#

在 Parcel 1 中,匯入任何非 JavaScript 檔案(例如圖片或影片)都會產生一個 URL。在 Parcel 2 中,這仍然適用於已知檔案類型(例如圖片),但沒有預設支援的其他檔案類型需要變更程式碼。

在 JavaScript 中參照 URL 的建議做法是使用 URL 建構函式。不過,您也可以選擇在 import 陳述式中,使用 url: 為相依項規格加上字首。

index.js:
import downloadUrl from "./download.zip";

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;
const downloadUrl = new URL('download.zip', import.meta.url);

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;

或者,您可以使用自訂的 .parcelrc 選擇舊行為。對您需要的副檔名使用 @parcel/transformer-raw 外掛程式和萬用字元。

yarn add @parcel/config-default @parcel/transformer-raw --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{zip,tgz}": ["@parcel/transformer-raw"]
}
}

轉譯

#

Parcel 1 自動將您的 JavaScript 轉譯為支援預設瀏覽器。Parcel 2 預設不再進行任何轉譯。這表示如果您在原始程式碼中使用現代 JavaScript 語法,Parcel 會輸出該語法。若要啟用轉譯,請將 package.json 中的 browserslist 欄位設定為定義您支援的瀏覽器目標。

package.json
{
"name": "my-project",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
},
"devDependencies": {
"parcel": "latest"
}
}

Babel

#

與 Parcel 1 相同,Parcel 2 會自動偵測 .babelrc 和其他 Babel 設定檔。但是,如果您只使用 @babel/preset-env@babel/preset-typescript@babel/preset-react,則可能不再需要 Babel。Parcel 自動支援所有這些功能,無需 Babel 設定檔,而且 Parcel 的預設轉譯器比 Babel 快得多。

如果您只使用上述預設值,您可以完全刪除 Babel 設定檔。這將改用 Parcel 的預設轉譯器,這應該會大幅提升您的建置效能。請務必在 package.json 中設定 browserslist,以符合 @babel/preset-env 先前使用的目標。

如果您在 Babel 設定檔中有自訂預設值或外掛程式,您可以保留這些設定檔,但移除上述列出的預設值。這也應該會提升效能(儘管幅度較小)。請參閱 JavaScript 文件中的 Babel,以取得更多詳細資訊。

在此範例中,.babelrc 僅包含 @babel/preset-env@babel/preset-react,因此可以刪除,並以 package.json 中的 browserslist 金鑰取代。

.babelrc:
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}],
"@babel/preset-react"
]
}
package.json:
{
"browserslist": "> 0.25%, not dead"
}

Typescript

#

Parcel 1 使用 tsc(官方 TypeScript 編譯器)轉譯 TypeScript。Parcel 2 現在改用 SWC,這大幅提升了轉譯效能。

不過,預設的轉譯器對 tsconfig.json 的支援有限。如果你使用自訂編譯器選項(超出 JSX 相關選項和 experimentalDecorators),你可以使用 @parcel/transformer-typescript-tsc 來取代 Parcel 預設的 TypeScript 轉換器。為此,請安裝預設設定檔和 TSC 外掛程式,並在專案根目錄建立一個 .parcelrc 檔案。

yarn add @parcel/config-default @parcel/transformer-typescript-tsc --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}

請參閱 TypeScript 文件,以取得更多關於使用 Parcel 搭配 TypeScript 的資訊。

Flow

#

就像 Parcel 1 一樣,當安裝 flow-bin 時,Parcel 2 會自動支援 Flow。這目前是使用 @babel/preset-flow 來實作。如果你有一個僅包含該預設設定檔的 Babel 設定檔,則可以按照 上方所述將其移除。

與 Parcel 1 不同,你的 Babel 設定檔會覆寫 Parcel 2 中的預設設定檔,而不是與其合併。如果你有 Flow 以外的自訂 Babel 外掛程式,則需要另外加入 @babel/preset-flow

匯入 GraphQL

#

當匯入 GraphQL 檔案 (.gql) 時,匯入仍然會解析/內嵌 (使用 graphql-import-macro),但現在你會取得已處理的 GraphQL 查詢,而不是 Apollo AST。

DataComponent.js:
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is the parsed AST

const DataComponent = () => {
const { data } = useQuery(fetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};
DataComponent.js:
import gql from "graphql-tag";
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is a string

// Convert to the Apollo Specific Query AST
const parsedFetchDataQuery = gql(fetchDataQuery);

const DataComponent = () => {
const { data } = useQuery(parsedFetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};

有了 Parcel 2 的新外掛程式架構,建立一個外掛程式來在建置時將字串剖析成 AST (就像 Parcel 1 所做的一樣) 非常容易。請參閱 轉換器 文件以取得詳細資訊。

package.json#main

#

許多 package.json 檔案 (例如由 npm init 產生的檔案) 都包含一個 main 欄位,而大多數工具 (對於非函式庫專案) 都會忽略此欄位。不過,當看到 main 欄位時,Parcel 會推斷你的專案是一個函式庫,並將其用作輸出路徑。對於大多數網路應用程式,應該移除這行。

package.json:
{
"main": "index.js",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}
package.json:
{
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}

如果你確實需要保留 main 欄位,而且希望 Parcel 忽略它,則可以在 package.json 中加入 "targets": { "main": false }。請參閱 函式庫目標 以取得詳細資訊。

CLI

#

--target

#

在 Parcel 1 中,--target CLI 選項控制您的程式碼編譯的環境。在 Parcel 2 中,這改為在 package.json 中設定。例如,設定 engines 欄位以包含 nodeelectron 金鑰,將相應地變更目標。

parcel build index.js --target node
package.json:
{
"engines": {
"node": "10"
}
}

您也可以在 Parcel 2 中同時建置多個目標。有關詳細資訊,請參閱 目標

--experimental-scope-hoisting

#

Parcel 2 預設啟用範圍提升。若要停用,請新增 --no-scope-hoist

parcel build index.js --experimental-scope-hoisting
parcel build index.js
parcel build index.js
parcel build index.js --no-scope-hoist

--bundle-node-modules

#

若要將套件從 node_modules 綑綁到目標 Node.js 時,您現在應該在目標設定中指定。

parcel build index.js --target node --bundle-node-modules
package.json:
{
"targets": {
"default": {
"includeNodeModules": true
}
},
"engines": {
"node": "10"
}
}

此選項比 CLI 參數更靈活。例如,您也可以選擇性地包含套件。有關詳細資訊,請參閱目標文件中的 includeNodeModules

--out-dir

#

--out-dir CLI 選項已重新命名為 --dist-dir,以符合 package.json 中的 distDir 選項。有關詳細資訊,請參閱 目標

parcel build index.html --out-dir www
parcel build index.html --dist-dir www

--out-file

#

--out-file CLI 選項已移除,路徑應改為在 package.json 中指定。有關詳細資訊,請參閱 多個目標函式庫目標

parcel build index.js --out-file lib.js
package.json:
{
"name": "my-library",
"version": "1.0.0",
"main": "lib.js"
}

--log-level

#

記錄層級現在有名稱,而不是數字 (noneerrorwarninfoverbose)。

parcel build index.js --log-level 1
parcel build index.js --log-level error

--global

#

此選項已移除,暫時沒有替代方案。

parcel build index.js --global mylib

--no-minify

#

此選項已重新命名為 --no-optimize

parcel build index.js --no-minify
parcel build index.js --no-optimize

API

#

使用 Parcel 2 編寫程式時,可以使用 @parcel/core 套件,而不是 parcel-bundler。API 已大幅變更。有關詳細資訊,請參閱 Parcel API

掛入套件事件

#

Parcel 1 讓您可以使用 API 掛入並聆聽事件,例如 buildEndbuildError。API 已變更,但您仍然可以聆聽事件,如下所示

index.js:
import Bundler from "parcel-bundler"

const bundler = new Bundler({ /* ... */ })
bundler.bundle()

bundler.on("buildEnd", () => { /* ... */ })
bundler.on("buildError", (error) => { /* ... */ })
import Parcel from "@parcel/core"

const bundler = new Parcel({ /* ... */ })

bundler.watch((err, buildEvent) => {
if (buildEvent.type === "buildSuccess") { /* ... */ }
if (buildEvent.type === "buildFailure") { /* ... */ }
})

外掛程式

#

外掛系統在 Parcel 2 中已完全更改。Parcel 1 外掛與 Parcel 2 不相容。有關 Parcel 2 外掛 API 的詳細資訊,請參閱 外掛系統

使用外掛

#

在 Parcel 1 中,將外掛安裝到專案中並將它們列在 package.json 相依性中即可自動啟用它們。在 Parcel 2 中,外掛會在 .parcelrc 中設定。有關詳細資訊,請參閱 Parcel 設定