为什么我们需要给 Angular library 创建多重入口 multiple entry point
原文:Creating Secondary Entry Points for your Angular Library
自從 Angular 庫(kù)功能(從 Angular 7 開始)發(fā)布以來,現(xiàn)在開發(fā) Angular 庫(kù)比以往任何時(shí)候都容易。 Angular 庫(kù)本身配備了一個(gè)名為 ng-packagr 的社區(qū)驅(qū)動(dòng)包,它幾乎是核心。 在本文中,我們將看看如何利用 ng-packagr 輔助入口點(diǎn)進(jìn)一步拆分我們的 Angular 庫(kù)!
Why do we need secondary entry points?
我們希望擁有輔助入口點(diǎn)的原因之一是使我們能夠拆分我們的依賴項(xiàng)。 讓我們看一個(gè)例子,其中一個(gè)模塊有 peerDependencies,而另一個(gè)沒有。
假設(shè)我們有如下的 library 文件夾結(jié)構(gòu):
library 名稱:my-awesome-lib
兩個(gè) module,awesome-plain 和 awesome-time
查看 awesome-plain Component 的實(shí)現(xiàn):
import { Component } from '@angular/core'; @Component({selector: 'awesome-plain',template: `<div>Hey I'm just a plain text with no dependencies!</div>` }) export class AwesomePlainComponent {}以及 awesome-time Component 的實(shí)現(xiàn):
import { Component } from '@angular/core'; import * as moment_ from 'moment'; const moment = moment_; @Component({selector: 'awesome-time',template: `<div>Hey, Awesome Time:</div><div>{{ time }}</div>` }) export class AwesomeTimeComponent {time: string;constructor() {this.time = moment().format();} }其中 plain Component 沒有任何依賴,而 time Component 依賴于 moment.
moment 依賴的定義在 library 的 package.json 里:
{"name": "my-awesome-lib","version": "0.0.1","peerDependencies": {"@angular/common": "^8.2.14","@angular/core": "^8.2.14","moment": "^2.26.0"} }請(qǐng)注意,這些 peerDependencies 放在庫(kù) my-awesome-lib 的范圍內(nèi),而不是放在單個(gè)模塊(庫(kù)內(nèi)的文件)上。
最后,以下是在 my-awesome-lib/src/public-api.ts 下如何導(dǎo)出 awesome-plain 和 awesome-time:
export * from './awesome-plain/awesome-plain.component'; export * from './awesome-plain/awesome-plain.module'; export * from './awesome-time/awesome-time.component'; export * from './awesome-time/awesome-time.module';The problem: Client needs to install ALL peer dependencies from the library
上面這樣設(shè)計(jì)的問題是什么?
假設(shè)我們有一個(gè) Angular 應(yīng)用程序想要使用 my-awesome-lib。 客戶端應(yīng)用程序(Angular 應(yīng)用程序)需要做的第一件事是安裝庫(kù):
npm i my-awesome-lib
安裝后,客戶端應(yīng)用程序然后繼續(xù)導(dǎo)入和使用,例如只有 awesome-plain 組件。 這是客戶端應(yīng)用程序中的代碼可能看起來很像:
// app.module.ts import { AwesomePlainModule } from 'my-awesome-lib'; @NgModule({...,imports: [...,AwesomePlainModule,],bootstrap: [AppComponent] }) export class AppModule {} // app.component.html <awesome-plain></awesome-plain>然而,ng serve 命令行會(huì)導(dǎo)致如下錯(cuò)誤:
ERROR in ./node_modules/my-awesome-lib/fesm2015/my-awesome-lib.js Module not found: Error: Can’t resolve ‘moment’ in ‘/app-showcase-v8/node_modules/my-awesome-lib/fesm2015’
它說它找不到安裝在客戶端應(yīng)用程序中的時(shí)刻。 嗯,這就是發(fā)生的事情。 盡管客戶端應(yīng)用程序僅導(dǎo)入并使用 awesome-plain,但 Angular 編譯器仍會(huì)要求安裝 my-awesome-lib 中定義的所有 peerDependencies,這在本例中很重要。
如果客戶端應(yīng)用程序同時(shí)使用 awesome-plain 和 awesome-time,當(dāng)前情況可能會(huì)很好。 然而,想象一下,如果庫(kù)變大并且有不止 2 個(gè)模塊,假設(shè)有 10 個(gè)模塊。 讓我們?cè)倏浯笠稽c(diǎn); 如果 10 個(gè)模塊中有 5 個(gè)具有不同的 peerDependencies 會(huì)怎樣? 如果有一個(gè)客戶端應(yīng)用程序使用這個(gè)庫(kù)并且只使用 1 個(gè)沒有任何 peerDependencies 的模塊,那么客戶端應(yīng)用程序仍然需要安裝所有 5 個(gè) peerDependencies! 當(dāng)然,應(yīng)該有比這更好的方法,對(duì)吧?
Enter the secondary entry points!
幸運(yùn)的是,很有可能優(yōu)化當(dāng)前的方法。 到目前為止,庫(kù)中使用的方法僅使用稱為主要入口點(diǎn)的東西。 這由 package.json 文件表示,該文件僅存在于 my-awesome-lib/package.json 下,其中定義了整個(gè)庫(kù)的所有 peerDependencies。
通過二級(jí)入口點(diǎn),我們可以進(jìn)一步將 peerDependencies 拆分到庫(kù)級(jí)別之外; 它使得在庫(kù)內(nèi)的文件夾或模塊中定義 peerDependencies 變得可行。
例如,通過將 awesome-time 設(shè)置為二級(jí)入口點(diǎn),我們可以在子目錄中創(chuàng)建另一個(gè) package.json 文件,該文件包含僅適用于 awesome-time 模塊的 peerDependencies。
因此,我們不再在庫(kù)級(jí)別定義 peerDependencies; 我們改為在
子目錄中定義它們。
此外,輔助入口點(diǎn)使我們能夠像下面這樣導(dǎo)入庫(kù):
// Primary entry points import { AwesomePlainModule } from 'my-awesome-lib'; // Secondary entry points import { AwesomeTimeModule } from 'my-awesome-lib/awesome-time';這樣,如果 Client App 只使用 AwesomePlainModule,編譯器就不會(huì)再要求安裝 moment 了!
Implement secondary entry points
希望上面的解釋能讓大家對(duì)我們?yōu)槭裁匆褂幂o助入口點(diǎn)有一個(gè)大致的了解。
好消息是,實(shí)現(xiàn)二級(jí)入口點(diǎn)相當(dāng)簡(jiǎn)單明了,因?yàn)?ng-packagr 將在幕后完成大部分工作!
我們將使用 my-awesome-lib 作為以下實(shí)施指南的上下文。 在這種情況下,我們將設(shè)置 awesome-time 作為次要入口點(diǎn),而 awesome-plain 將保持原樣(仍然是主要入口點(diǎn))。
(1) Place the folders for secondary entry points directly under the library folder.
根據(jù) ng-packagr 文檔,輔助入口點(diǎn)的文件夾布局示例之一是如下所示:
有趣的是,這是@angular/common 包中使用的類似文件夾布局,它以@angular/common 作為主要入口點(diǎn),而@angular/common/testing 作為次要入口點(diǎn)。
文件夾結(jié)構(gòu)如下:
(2) Create additional package.json and public-api.ts files in secondary entry points folder.
要?jiǎng)?chuàng)建輔助入口點(diǎn),我們需要告訴 ng-packagr 要查找哪個(gè)文件夾。 這可以通過在 /my-awesome-lib/awesome-time 文件夾下創(chuàng)建另一個(gè) package.json 和 public-api.ts 文件來實(shí)現(xiàn),除了主入口點(diǎn)的文件。 僅通過這樣做,ng-packagr 將動(dòng)態(tài)發(fā)現(xiàn)輔助入口點(diǎn)。
/my-awesome-lib/awesome-time/package.json 的內(nèi)容可以是:
請(qǐng)注意,到目前為止,我們將 moment 作為 peerDependencies 放置在這里。 此外,“umdModuleIds”用于在構(gòu)建庫(kù)時(shí)從 ng-packagr 中刪除警告。
以及 /my-awesome-lib/awesome-time/public-api.ts 的內(nèi)容如下:
/** Public API Surface of my-awesome-lib/awesome-time*/ export * from './awesome-time.component'; export * from './awesome-time.module';(3) Remove secondary entry points peer dependencies from the main package.json and secondary entry point exported files from the main public-api.ts.
{"name": "my-awesome-lib","version": "0.0.1","peerDependencies": {"@angular/common": "^8.2.14","@angular/core": "^8.2.14"} }moment 包已被刪除,因?yàn)樗言?/my-awesome-lib/awesome-time/package.json 中定義。
此外,我們將刪除在主 my-awesome-lib/src/public-api.ts 中導(dǎo)出的 awesome-time 文件。 該文件現(xiàn)在應(yīng)該只導(dǎo)出 awesome-plain 文件,如下所示:
/** Public API Surface of my-awesome-lib*/ export * from './awesome-plain/awesome-plain.component'; export * from './awesome-plain/awesome-plain.module';(4) Build the library.
現(xiàn)在一切都已設(shè)置完畢,我們現(xiàn)在可以嘗試通過執(zhí)行命令 ng build my-awesome-lib 來構(gòu)建庫(kù)。 如果正確完成,您應(yīng)該在終端中看到以下內(nèi)容:
另外,如果打開庫(kù)構(gòu)建文件夾 dist/my-awesome-library,文件夾內(nèi)應(yīng)該還有一些名為 my-awesome-lib-awesome-time..js 的文件,例如 dist/fesm2015 和 dist/bundles . 如果將它與沒有輔助入口點(diǎn)的那個(gè)進(jìn)行比較,構(gòu)建文件夾通常只包含 my-awesome-lib..js,這是僅針對(duì)庫(kù)本身的構(gòu)建。
(5) Install and import the library in the Client App.
最后一步是最終在 Angular 應(yīng)用程序中使用它。 由于我們從主要入口點(diǎn)移動(dòng)了 awesome-time,因此導(dǎo)入路徑會(huì)略有變化。 要在客戶端應(yīng)用程序中使用新的庫(kù)文件夾結(jié)構(gòu),它應(yīng)該如下所示:
// Primary entry points import { AwesomePlainModule } from 'my-awesome-lib'; // Secondary entry points import { AwesomeTimeModule } from 'my-awesome-lib/awesome-time';現(xiàn)在,如果客戶端應(yīng)用程序只使用 AwesomePlainModule,我們應(yīng)該可以在不安裝 moment 的情況下運(yùn)行應(yīng)用程序(僅在 AwesomeTimeModule 中使用)。
請(qǐng)記住,實(shí)施輔助入口點(diǎn)可能會(huì)導(dǎo)致您的 Angular 庫(kù)發(fā)生重大變化。 原因是因?yàn)槭褂媚膸?kù)的客戶端應(yīng)用程序必須更新導(dǎo)入路徑。 否則,他們的應(yīng)用程序?qū)⒅袛?#xff0c;因?yàn)楝F(xiàn)在不再?gòu)摹皔our-lib”導(dǎo)入輔助入口點(diǎn)文件。 因此,此更改不向后兼容。
Should there be any primary entry points at all? Is it okay to only have secondary entry points for the library?
您可能想知道,我們甚至應(yīng)該使用主要入口點(diǎn)嗎? 在我看來,只有次要入口點(diǎn)是可以的,主要是因?yàn)?#64;angular/material 只使用次要入口點(diǎn)。 另一方面,對(duì)于邏輯上相似的功能或特性,一般也建議使用主要入口點(diǎn)。 以下是在 Angular Package Format 文檔中編寫的:
Angular Package Format 的一般規(guī)則是為最小的邏輯連接代碼集生成 FESM 文件。 例如,Angular 包有一個(gè)用于@angular/core 的 FESM。 當(dāng)開發(fā)人員使用來自@angular/core 的 Component 符號(hào)時(shí),他們很可能也會(huì)直接或間接使用諸如 Injectable、Directive、NgModule 等符號(hào)。 因此,所有這些部分都應(yīng)該捆綁到一個(gè)單一的 FESM 中。 對(duì)于大多數(shù)庫(kù)情況,應(yīng)該將單個(gè)邏輯組組合到一個(gè) NgModule 中,并且所有這些文件應(yīng)該捆綁在一起作為包中的單個(gè) FESM 文件,代表 npm 包中的單個(gè)入口點(diǎn)。
此外,就我而言,經(jīng)驗(yàn)法則是將某些模塊作為輔助入口點(diǎn),如果它們具有不同的 peerDependencies。 這是為了防止客戶端應(yīng)用程序被迫手動(dòng)安裝所有依賴項(xiàng),盡管它們并未使用所有依賴項(xiàng)。
Conclusions
總而言之,次要入口點(diǎn)是一個(gè)巧妙的功能,它允許我們進(jìn)一步拆分 Angular 庫(kù),尤其是在處理 peerDependencies 時(shí)。 它也很容易實(shí)現(xiàn),因?yàn)?ng-packagr 將通過子目錄的 package.json 動(dòng)態(tài)發(fā)現(xiàn)輔助入口點(diǎn)。
好處之一是它會(huì)減少客戶端應(yīng)用程序被迫安裝所有依賴項(xiàng)的機(jī)會(huì),即使該應(yīng)用程序沒有導(dǎo)入/使用依賴于已安裝依賴項(xiàng)的任何庫(kù)函數(shù)。
更多Jerry的原創(chuàng)文章,盡在:“汪子熙”:
總結(jié)
以上是生活随笔為你收集整理的为什么我们需要给 Angular library 创建多重入口 multiple entry point的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 考试表情包搞笑图片大全
- 下一篇: tup股权激励什么意思