範本擴充性

範本擴充性是 PWA Kit v3 引進的一項功能。此功能的目標是讓您能夠藉由自訂範本,更輕鬆地建置 PWA Kit 專案。您可以自訂現有的基礎範本,例如 Retail React App,或者建立您自己的範本。我們鼓勵 PWA Kit 社群成員分享彼此的基礎範本。

範本擴充性可協助您修改選定的範本,而無需複製其中的每個檔案。

範本擴充性是一項選用功能,但是,2023 年 6 月 15 日之後建立的專案都會自動設定為使用範本擴充性。

在 PWA Kit v3 中,專案可以擁有一個基礎範本和一個覆寫目錄,這兩者可以在專案的 package.json 檔案中進行設定。若要使用範本擴充性,您必須在 package.json 中以 ccExtensibility.extends 宣告基礎範本,也必須以 ccExtensibility.overridesDir 宣告覆寫目錄。(設定詳情會在下一章節中說明。)

基礎範本是一個 npm 模組,包含一個功能完整的 PWA Kit 專案,此專案已預先設定完成,所以另一個 PWA Kit 專案可以直接覆寫其中部分檔案。有時候,基礎範本也稱為「可延伸應用程式」或「可擴充應用程式」。"

覆寫目錄是專案中的一個目錄,您可以在其中儲存要用來覆寫基礎範本相應檔案的檔案。

假設您已經定義了覆寫目錄,也已定義 Retail React App 作為您的基礎範本。舉例來說,您想要覆寫 Retail React App 的首頁元件。在 Retail React App 中,首頁元件的程式碼位於 @salesforce/retail-react-app 套件內的 app/pages/home/index.jsx 中。若要覆寫該檔案,您必須經由 package.json 中的 ccExtensibility.overridesDir,於宣告的目錄中重新建立一份相同檔案。因此,新檔案的路徑會是 <ccExtensibility.overridesDir>/app/pages/home/index.jsx

現在,每當 index.jsx 被匯入 @salesforce/retail-react-app 套件內的任何檔案,都會改為載入覆寫目錄中的檔案,而非原來套件中的檔案。要進行覆寫,唯有檔案名稱的基本部分需要相同,副檔名可以不同。

當您建置應用程式時,您可以逐漸從基礎範本新增檔案至覆寫目錄。

您覆寫的檔案越多,越需要費心來跟上基礎範本中的變更。

若要查看範本擴充性的實際運作方式,請執行 npx @salesforce/pwa-kit-create-app@latest --outputDir <path/to/new/local/project> 產生一個新的 PWA Kit v3 專案。

所產生的專案會使用 Retail React App 基礎範本,給您一個看起來大致與 https://pwa-kit.mobify-storefront.com/ 相同的應用程式。

本章節的指示假設您想要為現有的專案啟用範本擴充性,且先前建立該專案時並未啟用此功能。

若要定義基礎範本,請在專案的 package.json 檔案中新增 @salesforce/retail-react-app (或不同範本) 作為 npm 相依項目。

然後將一個 ccExtensibility 鍵新增至 package.json,使用下列鍵與值:

如果您正在使用的基礎範本不是 Retail React App,請別忘了將 @salesforce/retail-react-app 取代為其他範本的套件識別碼。

您可以將 overridesDir 的值取代為自訂目錄名稱,但為了維持各專案 (及我們的範例程式碼) 之間的一致性,我們建議使用 overrides

同一個相依項目不能同時位於主要專案和基礎範本中。這兩個 PWA Kit 應用程式必須不同,才能讓 @salesforce/pwa-kit-dev 區分出主要專案和基礎範本。(主要專案必須定義一個基礎範本,但不能直接沿用它本身。)

您的專案不能與基礎範本 package.json 檔案中列出的 npm 相依項目發生版本衝突。如果底層範本使用 @chakra-ui,則您的專案也必須將它當作相依項目,而且必須是相同版本。這種預防措施可防止因為同一套件的版本衝突,而意外使您的套件膨脹 (更別說會破壞功能性了)。此規則有一個例外:當您已經為基礎範本的所有檔案都新增了覆寫檔案時,就已匯入了既定的相依項目。在此情況下,系統永遠不會匯入基礎範本版本的相依項目,因為您已經免除了會經由覆寫而匯入相依項目的所有檔案。

在底層範本中新增其他未使用的 npm 相依項目則無妨。👌

每個部署到 Managed Runtime 的 PWA Kit 專案都必須擁有下列檔案:

  • <overridesDir>/app/main.jsx
  • <overridesDir>/app/ssr.js
  • <overridesDir>/app/routes.jsx
  • <overridesDir>/app/request-processor.js
  • config/default.js (例外:如果您正在使用 package.json 中的 mobify 鍵來儲存網站設定資料)

若要自訂專案的行為,通常會覆寫 @salesforce/retail-react-app 中的下列檔案:

  • app/pages/home/index.jsx:從首頁移除預設的 Salesforce 行銷內容,這幾乎是所有專案的第一步。
  • app/static/*:這些是提供圖示給桌面和行動瀏覽器使用的預設檔案,用以建立網站品牌。請更新它們以符合您的品牌。
  • app/constants.js
  • app/assets/svg/brand-logo.svg:此檔案會在產生者專案中被覆寫。請遵循此範例來覆寫您專案中的其他圖示。

這項「檔案系統即 API」設定的用意是協助您尋找底層檔案、複製它們,然後自訂其行為和邏輯。將程式碼拆解成較小的檔案,以便協調運用許多子元件,可以更容易只鎖定在您欲覆寫的行為上。

話雖如此,有時候您必須將某個檔案複製到覆寫檔案中,以便只變更該檔案的一部分行為。如果可能,最好是匯入您所匯出的底層範本,然後將它們重新匯出,如此您就不需要在延伸專案中手動更新程式碼,即可套用對範本版本所做的變更。

例如,在所產生專案routes.jsx 檔案中,我們可以發現,預設路由已透過 import {routes as _routes} from '@salesforce/retail-react-app/app/routes' 匯入,其中的 _routes 作為使用慣例,指出預設路由是由外部相依項目加以管理。就像 @salesforce/retail-react-app/app/routes.jsx 檔案,我們的自訂實作 routes.jsx 可匯出路由;其新增了 / (首頁) 路由。因為 React Router 會在路由陣列中尋找給定路徑名稱的第一個項目,所以新的路由會優先於預設路由。

對大部分使用範本擴充性的專案而言,相對匯入是最好的方法。基礎範本 (例如 @salesforce/retail-react-app) 中,檔案的匯入行為採用特殊邏輯,會優先考慮覆寫目錄中的檔案。此匯入邏輯不會套用至您覆寫目錄中的檔案。因此,我們建議使用相對匯入來進行大多數實作。

範本擴充性需要注意的一個問題是意外匯入兩個衝突的不同檔案。PWA Kit 專案是透過 @salesforce/pwa-kit-dev 建置而成,其內部使用 Webpack@salesforce/pwa-kit-dev 中有一個外掛程式用來啟用範本擴充性的「覆寫」功能。這個外掛程式會檢驗所有的 Webpack 檔案要求,並詢問兩個問題:

  1. 這個檔案要求來自基礎範本嗎?
  2. 這個檔案存在於覆寫目錄中嗎?

如果兩個問題的答案都是「是」,則檔案要求會在建置時間重新寫入,指向覆寫目錄中的檔案。

這個邏輯不會對來自覆寫目錄的 Webpack 檔案要求產生作用。因此,可能會同時從覆寫目錄和基礎範本匯入相同的檔案。例如,如果這種情況發生在同一個套件中匯入 @saleforce/retail-react-app/my-file<overrides directory>/my-file 時,Webpack 會丟出類似於 can’t find <export name> from <filename> 的令人困惑的錯誤。Webpack 會丟出這個錯誤,因為套件不知道哪個匯入才是套件要納入的預定目標。

當覆寫目錄中存在同等檔案時,避免從基礎範本中匯入檔案是很重要的,不過這裡有一個值得注意的例外。如先前在特殊檔案章節的程式碼範例中所述並透過 constants.js 所示範,較好的做法是匯入底層範本後再重新匯出其所有匯出,然後視需要覆寫或附加盡可能少的匯出。

當您需要覆寫 app/components/_app/index.jsx 元件時,其中有許多全域元件,例如 HeaderFooterDrawerMenu。在覆寫了 _app/index.jsx 之後,基礎範本欲匯入此元件的任何嘗試都會將檔案要求重新路由至 <ccExtensibility.overridesDir>/app/components/_app/index.jsx。但這有一個陷阱!因為 HeaderFooter 是由 app/components/_app/index.jsx 所匯入,所以也必須在 _app/index.jsx 中更新匯入!否則,_app/index.jsx 會從基礎範本匯入 HeaderFooter

來自 ccExtensibility.extends 的 ECMAScript 匯入很「神奇」,因為 @salesforce/pwa-kit-dev 會在多個位置檢查同名檔案,其中 ccExtensibility.overridesDir 較為優先。

另一方面,來自 <ccExtensibility.overridesDir>/* 的匯入沒有神奇的行為,因此您必須針對您想要的檔案,<ccExtensibility.overridesDir>/app/components/header/index.jsx 必須在 <ccExtensibility.overridesDir>/app/components/_app/index.jsx 中明確匯入,如下所示:

請勿像這樣從套件中匯入,因為它會繞過覆寫目錄中的標頭:

請注意,routes.jsx 是一個特殊檔案。它形成 Webpack 詞彙中的「entryPoint」(進入點),適用於整個應用程式。如果您在 routes.jsx 中的任何內容設定不正確,會導致整個應用程式在編譯階段失敗,因為不能正確解析 pwa-kit-dev 中以路由為基礎的分塊。因此,我們提供了一個範例,說明如何在 overridesDir 中混合來自預設 @salesforce/retail-react-app/app/routes.jsx 和本機 routes.jsx 的匯入來正確延伸路由。

由於此處的匯入「位於樹狀結構頂層」,所以如果您覆寫了這些檔案然後覆寫 footer.jsx,則您必須返回 _app/index.jsx,修改該匯入以指向相對的範本匯入。

範本擴充性系統的一項已知限制是,憑藉神奇的拉入功能,以 retail-react-app/constants 為例,每當基礎範本 (@salesforce/retail-react-app) 匯入此檔案時,都預期會匯出相同的值。

覆寫目錄中的檔案若無法匯出與基礎範本中同等檔案相同的 ECMAScript 匯出,可能會導致意外錯誤,如下所示。

像這樣將 constants.js 檔案新增至覆寫目錄時,會導致錯誤:

範例錯誤訊息:

發生此錯誤的原因在於,CAT_MENU_DEFAULT_ROOT_CATEGORY@salesforce/retail-react-app/components/_app/index.jsx 中的預期匯出,而在使用上述唯一匯出 CUSTOM_MESSAGE 覆寫它的過程中,您的覆寫破壞了 constants.js 所匯出的底層「API 合約」(檔案依賴某個特定值的能力,即本例中的 CAT_MENU_DEFAULT_ROOT_CATEGORY)。

正確的方法應該如下。此方法可避免漏掉 CAT_MENU_DEFAULT_ROOT_CATEGORY 作為必要的預期匯出,因此我們能維持所匯出 API 的一致性。

此方法僅適用於新增變更。在下方的範例中,我們新增了一個 CUSTOM_MESSAGE 匯出,它不在底層 retail-react-app/constants.js 檔案的匯出中。

此方法適用於以下情境,即 DEFAULT_LOCALE 是由 retail-react-app/constants 匯出,而我們要改變這個值:

範本 Hook 是與 Retail React App 範本互動的一個新方法,過去可透過 npx pwa-kit-create-app 命令 (現在更名為 npx @salesforce/pwa-kit-create-app) 使用。

作為範本擴充性功能的一部分,新的「範本 Hook」已新增至 @salesforce/retail-react-app,支援全域新增一小部分元件,如下面的清單所示。根據預設,這些元件中的每一個都會傳回 null,並刻意保持空白,以避免非必要地膨脹已完成的 PWA Kit 實作專案。基礎 @salesforce/retail-react-app 永遠不會對這些元件新增任何功能。範本 Hook 一律傳回 null,以確保客戶實作可以在範本內「勾住」這些要自訂專案的地方,而無需覆寫不必要的檔案。

@salesforce/retail-react-app@1.0.0 中,可以使用下列範本 Hook:

  • app/components/_app/partials/above-header.jsx
  • app/pages/product-list/partials/above-page-header.jsx

在 PWA Kit v3 推出時,只有一個公開的可延伸基礎範本:@salesforce/retail-react-app (預計之後會提供更多)。

若要當作基礎範本使用,PWA Kit 專案必須發佈至 npm,且其所有 ECMAScript 匯入必須以符合套件名稱的值為前綴。例如,@salesforce/retail-react-app 已在 npm 上發佈,且其匯入都以 @salesforce/retail-react-app 為前綴。此前綴參照至套件的根目錄,這是必要的設定,如此才能作為基礎範本妥善運作。

若要將 PWA Kit 專案當作可延伸的基礎範本,則它所有的匯入都必須使用 <npm package name>。例如,@salesforce/retail-react-appccExtensibility 新增至 package.json,以確保本機 IDE 參照 (例如 @salesforce/retail-react-app/app/components/_app) 可以正確解析。