SAP Spartacus 的延迟加载 Lazy load 设计原理
官方鏈接
延遲加載,也稱為代碼拆分,可讓您將 JavaScript 代碼分成多個(gè)塊。 結(jié)果是當(dāng)用戶訪問(wèn)第一頁(yè)時(shí),您不必加載完整應(yīng)用程序的所有 JavaScript。 相反,只加載給定頁(yè)面所需的塊。 在導(dǎo)航到其他頁(yè)面時(shí),會(huì)在需要時(shí)加載其他塊。
這種方法可以顯著改善“交互時(shí)間”,尤其是在低端移動(dòng)設(shè)備訪問(wèn)復(fù)雜 Web 應(yīng)用程序的情況下。
Spartacus Approach to Lazy Loading
代碼拆分是一種必須在應(yīng)用程序構(gòu)建時(shí)完成的技術(shù)。 Angular 提供的代碼拆分通常是基于路由的,這意味著著陸頁(yè)有一個(gè)塊,產(chǎn)品頁(yè)面有另一個(gè)塊,依此類推。
由于 Spartacus 主要是 CMS 驅(qū)動(dòng)的,因此無(wú)法在構(gòu)建時(shí)決定每個(gè)路由的實(shí)際應(yīng)用邏輯。業(yè)務(wù)用戶最終將通過(guò)引入或刪除組件來(lái)改變頁(yè)面結(jié)構(gòu)。 這就是為什么需要另一種延遲加載方法的原因,Spartacus 通過(guò)以下方式提供:
- CMS 組件的延遲加載
- CMS 驅(qū)動(dòng)的功能模塊延遲加載 - CMS-driven lazy loading of feature modules
Defining Dynamic Imports Only in the Main Application
動(dòng)態(tài)導(dǎo)入是一種用于促進(jìn)延遲加載并允許代碼拆分的技術(shù),只能在主應(yīng)用程序 - main application 中使用。 無(wú)法在預(yù)構(gòu)建庫(kù)中定義動(dòng)態(tài)導(dǎo)入。
這是一個(gè)不幸的限制,導(dǎo)致必須由客戶添加一些應(yīng)用程序代碼。 盡管自定義代碼的數(shù)量被限制在最低限度,但我們將在未來(lái)版本的 schematics library 中添加一項(xiàng)功能,以自動(dòng)添加延遲加載模塊。
Avoiding Static Imports for Lazy-Loaded Code
為了使代碼拆分成為可能,您的靜態(tài) JavaScript 代碼(主應(yīng)用程序包)不應(yīng)該對(duì)您想要延遲加載的代碼進(jìn)行任何靜態(tài)導(dǎo)入。 如果你真的這么做了,構(gòu)建器會(huì)注意到代碼已經(jīng)包含在內(nèi),因此不會(huì)為其生成單獨(dú)的塊。 這在從庫(kù)中導(dǎo)入符號(hào)的情況下尤其重要。
在撰寫(xiě)本文時(shí)(Angular 9 和 Angular 10),將靜態(tài)導(dǎo)入與動(dòng)態(tài)導(dǎo)入混合用于相同的庫(kù)入口點(diǎn),即使對(duì)于不同的符號(hào),也會(huì)破壞該庫(kù)入口點(diǎn)的延遲加載和 tree shaking. 如果您要這樣做,它將在構(gòu)建中靜態(tài)地包含整個(gè)入口點(diǎn)。 因此,強(qiáng)烈建議您為必須靜態(tài)加載的代碼創(chuàng)建特定的入口點(diǎn),并為可以延遲加載的代碼創(chuàng)建單獨(dú)的入口點(diǎn)。
Configuration in Lazy-Loaded Modules
如果延遲加載模塊內(nèi)部提供了額外的配置,Spartacus 會(huì)將其合并到全局應(yīng)用程序配置中,以支持現(xiàn)有組件和服務(wù)的延遲加載場(chǎng)景。 在大多數(shù)情況下,尤其是當(dāng)延遲加載的模塊主要提供默認(rèn)配置時(shí),這可以可靠地工作。 但是,如果過(guò)度使用它會(huì)導(dǎo)致問(wèn)題,尤其是當(dāng)兩個(gè)模塊為配置的同一部分提供不同的配置時(shí)。 可以通過(guò)在主應(yīng)用程序中提供必要的覆蓋來(lái)修復(fù)諸如此類的場(chǎng)景。
這種合并功能是通過(guò)默認(rèn)啟用的兼容性機(jī)制實(shí)現(xiàn)的,但您可以使用 disableConfigUpdates 功能標(biāo)志禁用它。 如果您正在開(kāi)發(fā)必須從延遲加載的模塊中掛鉤到配置的新模塊,則應(yīng)改用 ConfigurationService.unifiedConfig$。 此功能在下一節(jié)中描述。
Unified Configuration
統(tǒng)一配置提供了一種獲取全局配置的方法,該配置包括根配置和來(lái)自已加載延遲加載模塊的配置。
ConfigurationService.unifiedConfig$ 將統(tǒng)一配置公開(kāi)為每次更改時(shí)發(fā)出新配置的 observable。 例如,每次加載和實(shí)例化具有提供的配置的延遲加載模塊時(shí)都會(huì)發(fā)生這種情況。
所有配置部分都按照嚴(yán)格的順序合并,實(shí)際配置始終覆蓋默認(rèn)配置,并且根模塊(即app shell)中定義的配置具有優(yōu)先權(quán)。
以下示例顯示了根應(yīng)用程序和兩個(gè)延遲加載模塊中提供的不同配置的合并順序,其中列表中的每個(gè)后續(xù)項(xiàng)都可以覆蓋前一項(xiàng):
- 默認(rèn)根配置
- 延遲加載模塊 1 的默認(rèn)配置
- 延遲加載模塊 2 的默認(rèn)配置
- 延遲加載模塊 1 的配置
- 延遲加載模塊 2 的配置
- 根配置(始終優(yōu)先)
Providers in Lazy-Loaded Modules
延遲加載模塊中提供的注入令牌對(duì)根應(yīng)用程序中提供的服務(wù)不可見(jiàn)。 這尤其適用于多提供的令牌,例如 HttpInterceptors、各種處理程序等。
為了減輕這個(gè)缺點(diǎn),一些 Spartacus 功能,例如 PageMetaService(使用 PageMetaResolver 令牌)或 ConverterService(主要使用適配器序列化器和規(guī)范化器),后臺(tái)使用統(tǒng)一注入器。 通過(guò)這樣做,他們可以訪問(wèn)延遲加載的令牌,并可以利用它們來(lái)實(shí)現(xiàn)全局功能。
對(duì)于不依賴于統(tǒng)一注入器的機(jī)制(例如,來(lái)自大多數(shù)非 Spartacus 庫(kù)的功能,例如核心 Angular 庫(kù)),建議您始終使用這些類型的令牌預(yù)先加載模塊。
unified injector
統(tǒng)一注入器提供了一種注入令牌或多提供令牌的方法,同時(shí)考慮到根注入器和來(lái)自延遲加載功能的注入器。 注入器公開(kāi)一個(gè)可觀察的對(duì)象,每次統(tǒng)一注入器的狀態(tài)發(fā)生變化時(shí),該觀察對(duì)象都會(huì)為指定的令牌發(fā)出一組新的可注入對(duì)象。
Avoiding Importing the HttpClientModule in Your Lazy-Loaded Modules
一般來(lái)說(shuō),HttpClientModule 應(yīng)該在根應(yīng)用程序中導(dǎo)入,而不是在庫(kù)中。 例如,如果您將它導(dǎo)入到延遲加載的庫(kù)中,則根庫(kù)中的所有注入器對(duì)于源自延遲加載模塊的 HTTP 調(diào)用都是不可見(jiàn)的。
雖然技術(shù)上可以在庫(kù)中導(dǎo)入 HttpClientModule ,但在大多數(shù)情況下這不是預(yù)期的,并且可能導(dǎo)致難以解釋的錯(cuò)誤,因此請(qǐng)記住這一點(diǎn)。
Lazy Loading of CMS Components
Configuration of Lazy Loading CMS Components
CMS 代碼的延遲加載是通過(guò)在 CMS 映射配置中指定動(dòng)態(tài)導(dǎo)入代替靜態(tài)引用的組件類來(lái)實(shí)現(xiàn)的。 下面是一個(gè)例子:
{cmsComponents: {SimpleResponsiveBannerComponent: {component: () => import('./lazy/lazy.component').then(m => m.LazyComponent)}} }Technical Details
CMS 組件映射中對(duì)動(dòng)態(tài)導(dǎo)入的支持是使用可定制的組件處理程序(特別是 LazyComponentHandler)實(shí)現(xiàn)的。
可以擴(kuò)展此處理程序以自定義其行為、添加特殊鉤子或不同的觸發(fā)器,或者實(shí)現(xiàn)可以選擇性地重用現(xiàn)有處理程序的全新處理程序。
Lazy Loading of Modules
- 懶加載不僅是組件代碼,還有核心部分(包括NgRx狀態(tài))
- 在第一次需要時(shí)只加載一次功能
- 提供共享的、延遲加載的依賴模塊
- 當(dāng)實(shí)現(xiàn)被相關(guān)功能配置覆蓋時(shí),CMS 請(qǐng)求組件會(huì)觸發(fā)功能模塊的延遲加載。
Configuration of Lazy-Loaded Modules
-
功能模塊的動(dòng)態(tài)導(dǎo)入(必須在主應(yīng)用程序中定義)
-
有關(guān)特定功能涵蓋哪些 CMS 組件的信息(可以成為庫(kù)的一部分并靜態(tài)導(dǎo)入)。 此信息以 cmsComponents 鍵下的字符串?dāng)?shù)組的形式提供。 下面是一個(gè)例子:
例子:
[圖片上傳中…(image.png-c043ed-1625895062179-0)]
Component Mapping Configuration in Lazy-Loaded Modules
延遲加載模塊中的默認(rèn) CMS 映射配置應(yīng)該以與靜態(tài)導(dǎo)入模塊完全相同的方式定義。
Spartacus 能夠從延遲加載功能中提取 CMS 組件映射配置,并使用它來(lái)解析該功能所涵蓋的組件類和工廠。 這就是為什么可以并推薦使用在延遲加載模塊中提供默認(rèn) CMS 映射配置的標(biāo)準(zhǔn)方式的原因。 因此,完全相同的模塊和庫(kù)入口點(diǎn)可以根據(jù)需要?jiǎng)討B(tài)或靜態(tài)導(dǎo)入,并且仍然可以通過(guò)在應(yīng)用程序中提供配置覆蓋來(lái)從應(yīng)用程序級(jí)別覆蓋延遲加載的 CMS 配置。
Defining Shared-Dependency Modules
通過(guò)在功能配置的依賴項(xiàng)屬性中提供動(dòng)態(tài)導(dǎo)入數(shù)組,可以將一些邏輯提取到共享的延遲加載模塊中,該模塊可以定義為功能模塊的延遲加載依賴項(xiàng)。 下面是一個(gè)例子:
{featureModules: {organization: {module: () =>import('@spartacus/my-account/organization').then((m) => m.OrganizationModule),dependencies: [() =>import('@spartacus/storefinder/core').then((m) => m.OrganizationModule),// ,,],},}, }當(dāng)延遲加載依賴它的第一個(gè)特性時(shí),這種未命名的依賴模塊只會(huì)被實(shí)例化一次。 它的提供者為傳遞給特征模塊的組合注入器做出貢獻(xiàn),因此,所有特征服務(wù)和組件都可以訪問(wèn)依賴模塊提供的服務(wù)。
Combined Injector
任何延遲加載的模塊都可以從根應(yīng)用程序注入器和依賴模塊注入器注入(即可以訪問(wèn))服務(wù)和令牌。 這是可能的,因?yàn)槊看螌?shí)例化具有依賴項(xiàng)的功能模塊時(shí)都會(huì)創(chuàng)建 CombinedInjector。
當(dāng)一個(gè)被延遲加載模塊覆蓋的 CMS 組件被實(shí)例化時(shí),它可以注入(即訪問(wèn))以下服務(wù):
- ModuleInjector 層次結(jié)構(gòu),從功能模塊注入器開(kāi)始,包括依賴模塊和根注入器
- ElementInjector 層次結(jié)構(gòu),在每個(gè) DOM 元素上隱式創(chuàng)建
Initializing Lazy Loaded Modules
Spartacus 提供了一個(gè) MODULE_INITIALIZER 來(lái)代替 Angular APP_INITIALIZER 來(lái)初始化延遲加載的模塊。
APP_INITIALIZER 機(jī)制在任何延遲加載發(fā)生之前完成應(yīng)用程序的初始化,因此在加載時(shí)可能需要運(yùn)行初始化邏輯的延遲加載功能無(wú)法這樣做。
MODULE_INITIALIZER 注入令牌可用于在旨在延遲加載的模塊中提供初始化函數(shù)。 MODULE_INITIALIZER 由 Spartacus 延遲加載機(jī)制支持,因此,使用 MODULE_INITIALIZER 提供的初始化函數(shù)將在它們定義的模塊被延遲加載之前運(yùn)行。
您可以像配置 APP_INITIALIZER 一樣配置 MODULE_INITIALIZER。 下面是一個(gè)例子:
...import { MODULE_INITIALIZER } from '@spartacus/core';...export function myFactoryFunction(dependencyOne: DependencyOne ) {const result = () => {// add initialization logic here};return result; }@NgModule({providers: [{provide: MODULE_INITIALIZER,useFactory: myFactoryFunction,deps: [DependencyOne],multi: true,},], }) export class MyLazyLoadedModule {}Preparing Libraries to Work with Lazy Loading
Providing Fine-Grained Entry Points in Your Library
在您的庫(kù)中提供細(xì)粒度的入口點(diǎn)。
從相同的入口點(diǎn)混合靜態(tài)和動(dòng)態(tài)導(dǎo)入會(huì)破壞延遲加載并影響 tree-shaking,因此任何將直接用于動(dòng)態(tài)導(dǎo)入的庫(kù)都應(yīng)該公開(kāi)細(xì)粒度的輔助入口點(diǎn)以優(yōu)化代碼拆分。
作為慣例,Spartacus 暴露功能的根入口點(diǎn),例如@spartacus/orgainzation/administration/root。 這種類型的入口點(diǎn)包含所有不應(yīng)或不能延遲加載的代碼。 來(lái)自根入口點(diǎn)的模塊應(yīng)該在根應(yīng)用程序中靜態(tài)導(dǎo)入,這意味著它們將被預(yù)先加載并在主應(yīng)用程序塊中可用。
Separating Static Code from Lazy-Loaded Code
當(dāng)您使用 Angular Dependency Injection 時(shí),注入器中的提供程序列表在注入器初始化后不應(yīng)更改。這種范式特別適用于任何多提供的令牌、處理程序,尤其適用于任何 Angular 原生多提供的令牌,例如 HTTP_INTERCEPTOR、APP_INITIALIZER 等。
結(jié)果是延遲加載模塊中的任何多提供令牌對(duì)于根或其他延遲加載塊中提供的模塊和服務(wù)將不可見(jiàn),但使用 unified injector 注入的多提供令牌除外。
一些 Spartacus 功能,例如 PageMetaService 或 ConverterService,使用 UnifiedInjector 來(lái)了解可以延遲加載的令牌,以便全局邏輯(例如 SEO 功能)即使邏輯延遲加載該功能也能可靠地工作。例如,商店定位器頁(yè)面元解析器可以在使用商店定位器功能的前提下,被延遲加載。
Spartacus 配置也是通過(guò)提供配置塊來(lái)定義的,由于兼容機(jī)制將配置從延遲加載功能貢獻(xiàn)到全局配置,因此處理方式略有不同。這種機(jī)制可以通過(guò)特性標(biāo)志禁用,將來(lái)會(huì)默認(rèn)關(guān)閉,以支持統(tǒng)一配置特性。
如果根服務(wù)無(wú)法看到延遲加載提供程序的問(wèn)題,則始終可以將此類代碼包含在預(yù)先可用的靜態(tài)鏈接模塊中。建議在您的庫(kù)中創(chuàng)建一個(gè)單獨(dú)的入口點(diǎn)(按照慣例,命名為 root,例如 my-library/root),其中包含最少的代碼,將包含在主包中,并且從一開(kāi)始就可用。
下面是客戶項(xiàng)目里 Lazy Load 的一個(gè)具體例子:
export const lazyLoadModules = {home: {module: () =>import('./custom/cms-components/home/home.module').then((m) => m.HomeModule),cmsComponents: ['ConnectWithUsComponent','StayConnectedComponent','DiabloUniverseBannerComponent','DiabloSupportBannerComponent','DiabloCommunityBannerComponent','WelcomeUserComponent','DiabloRewardsBannerComponent','ExclusiveForYouComponent','FeatureProductCarouselComponent','FeatureProductsComponent','TopBannerProductsComponent','ExclusiveForYouHeaderComponent','HomepageAdvantageHeadingParagraphComponent','DiabloAdvantagesCarouselBannerComponent',],},wishlist: {module: () =>import('./custom/cms-components/wish-list/custom-wish-list.module').then((m) => m.CustomWishListModule),cmsComponents: ['CustomWishListComponent'],},checkoutLogin: {module: () =>import('./custom/cms-components/user/custom-checkout-login/custom-checkout-login.module').then((m) => m.CustomCheckoutLoginModule),cmsComponents: ['CustomCheckoutLoginComponent'],},login: {module: () =>import('./custom/cms-components/user/custom-login/custom-login.module').then((m) => m.CustomLoginModule),cmsComponents: ['CustomLoginComponent'],},loginForm: {module: () =>import('./custom/cms-components/user/custom-login-form/custom-login-form.module').then((m) => m.CustomLoginFormModule),cmsComponents: ['CustomLoginFormComponent'],},orderConfirmation: {module: () =>import('./custom/cms-components/order-confirmation/order-confirmation.module').then((m) => m.OrderConfirmationModule),cmsComponents: ['CustomOrderConfirmationOverviewComponent','CustomOrderConfirmationThankYouMessageComponent','CustomOrderConfirmationItemsComponent','CustomOrderConfirmationTotalsComponent',],},header: {module: () =>import('./custom/cms-components/navigation/header/custom-header.module').then((m) => m.CustomHeaderModule),cmsComponents: ['CustomCategoryNavigationComponent','CustomHamburgerMenuComponent','CustomNavigationUIComponent','CustomSearchBoxComponent','DiabloRewardsComponent','CustomHeaderNavigationComponent',],},footer: {module: () =>import('./custom/cms-components/navigation/footer/custom-footer.module').then((m) => m.CustomFooterModule),cmsComponents: ['CustomFooterNavigationComponent','ExtendedFooterNavigationComponent','CustomFooterNavigationUIComponent',],},product: {module: () =>import('./custom/cms-components/product/product.module').then((m) => m.ProductModule),cmsComponents: ['NewSearchBoxComponent','EmptySearchMessageComponent','SearchSuggestionsMessageComponent','ContactUsComponent','SupportComponent','ChatComponent','ChatDetailsComponent','ChatLinkComponent','EmailComponent','EmailDetailsComponent','EmailLinkComponent','PhoneComponent','PhoneNumberComponent','PhoneLinkComponent','PhoneDetailsComponent','CommunityComponent','CommunityDetailsComponent','CommunityLinkComponent','InternationalAssistanceComponent','InternationalAssistanceDetailsComponent','PDPTabNavComponent','ProductImagesComponent','ProductIntroComponent','ProductSummaryComponent','AddToWishListComponent','ProductHighlightsComponent','ProductOffersComponent','PDPReturnPolicyComponent','supportTabComponent','WarrantyTabComponent','PDPServiceComponents','ProductReviewComponent','CrossSelling','Similar','PDPQuestionsComponent','ProductLegalComponent','ProductSpecificationComponent','PDPFeatureSlotComponent','Frequently_Bought_Together',],}, };// CustomConsentManagementModule, // ProductModule, // MyAccountModule, // CustomCheckoutModule, // CustomCardModule, // CustomMainModule, // CustomIconModule, // CustomNotificationPreferenceModule, // CustomBreadcrumbModule, // CustomOccModule, // CustomCmsOccModule, // CustomFooterModule, // CustomLoginModule, // CustomHeaderModule, // CustomMiniCartModule, // CustomGenericLinkModule, // CustomCartDetailsModule, // OrderConfirmationModule, // CustomCartCouponModule, // CustomCartTotalsModule, // CustomCartSharedModule, // CustomItemCounterModule, // GenericParagraphModule, // CustomLoginFormModule, // IconModule, // HomeModule, // CustomWishListModule, // CustomCheckoutLoginModule, // CustomCheckoutOccModule, // CustomAuthStoreModule, // BrowserTransferStateModule, // CustomCmsParagraphModule, // I18nModule,總結(jié)
以上是生活随笔為你收集整理的SAP Spartacus 的延迟加载 Lazy load 设计原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cad怎么查看块的数量(CAD怎么创建块
- 下一篇: adni数据集简介_ADP数据