使用 ng-packagr 打包 Angular
寫在前面
為了讓 Angular 類庫應用範圍更自由,Angular 提出一套打包格式建議名曰:Angular Package Format,包括 FESM2015、FESM5、UMD、ESM2015、ESM5、ES2015 格式,不同格式可以在不同的環境(Angular Cli、Webpack、SystemJS等)中使用。
傳統方式需要對這些格式逐一打包,一個示例打包指令碼寫法。這種寫法只能針對不同專案的配置,而且除非你瞭解這些格式的本質否則很難維護;後來社群根據 APF 規範實現了類庫 ng-packagr,通過簡單的配置可以將你的類庫打包成 APF 規範格式。
如何使用
既然 ng-packagr 被 Angular Cli 內建,這讓我們進一步簡化了生產一個 APF 規範格式的類庫的成本。在 Angualr Cli 裡使用 ng g library
ng g library <library name>
而打包,則:
ng build <library name>
最終,將生成的 dist/<libary name>
目錄下檔案上傳相應包管理伺服器(例如:npm)提供給其他 人使用。
配置說明
由 Angular Cli 生成的類庫模板大部分內容同 Angular 應用一樣,只是多了一個 ng-package.json
的配置檔案(對於生產環境是 ng-package.prod.json
),它是專門針對 ng-packagr 的一個配置檔案,如同 angular.json 一般也是基於 JSON Schema 格式,因此可以通過訪問
whitelistedNonPeerDependencies
ng-packagr 預設會根據 package.json 的 peerDependencies
節點清單來決定類庫所需要第三方依賴包,這些依賴包是不會被打包至類庫。
然而,所依賴包不存在 peerDependencies
節點裡時(當然建議需要依賴的項應該在裡面),就需要該屬性的配置。
lib/entryFile
指定入口檔案。
lib/umdModuleIds
UMD 格式採用 rollup 打包,當類庫需要引用一些無法猜出正確 UMD 識別符號時,就需要你手動對映這些類庫的標識。
"umdModuleIds": {
"lodash": "_"
}
angular.json
Angular Cli 配置檔案 angular.json 內會增加一個以 <libary name>
命名的構建配置,絕大多數配置性同普通 Angular 應用如出一轍,唯一不同的是 builder
節點為:
"builder": "@angular-devkit/build-ng-packagr:build"
次級入口
有時候一個類庫可能會包含著多個二次入口,就像 @angular/core
類庫包含著一個 @angular/core/testing
模組,它只是運用於測試,因此並不希望在專案中引入 @angular/core
時也包含測試程式碼,但同時二者又是同一個功能性時,這種次級匯入顯得非常重要。
另一種像 ngx-bootstrap、@angular/cdk/ally 等都提供次級模組的匯入,可以更好的優化體積。
不論出於何種目的,都可以通過 Angular Cli 簡單的檔案組織進一步打包出主、次級分明的類庫。
ng g library
生成的結構大概如下:
<libary name>
├── src
| ├── public_api.ts
| └── lib/*.ts
├── ng-package.json
├── ng-package.prod.json
├── package.json
├── tsconfig.lib.json
└── tsconfig.spec.json
當根目錄下包含README.md
、LICENSE
時會自動被複制到dist
目錄中,Npm 規定必須包含 README.md 檔案,否則訪問已釋出類庫頁時會有未找到描述檔案錯誤提示。
若想建立一個 <libary name>/testing
的次級入口,只需要在 <libary name>
根目錄下建立一個 testing
目錄:
<libary name>
├── src
| ├── public_api.ts
| └── lib/*.ts
├── ng-package.json
├── ng-package.prod.json
├── package.json
├── tsconfig.lib.json
├── tsconfig.spec.json
└── testing
├── src
| ├── public_api.ts
| └── *.ts
└── package.json
核心是需要提供一個 package.json
檔案,而且內容簡單到姥姥家。
{
"ngPackage": {}
}
最後,依然使用 ng build <libary name>
,會產生一個次級匯入模組。
小結
至此,基本上利用 Angular Cli 可以快速的構建一個可釋出於 Npm Angular 類庫,更復雜的可以構建像 ngx-bootstrap、@angular/cdk/* 類庫。
自定義構建
Angular Cli 雖然提供非常便利的環境,但是對於一些複雜環境像 Delon 類庫(ng-alain基建系列類庫)包含著多個類庫、類庫又包含多個次級匯入時,Angular Cli 會顯得有點囉嗦,特別是對每個類庫的 angular.json 配置。其實 @angular-devkit/build-ng-packagr 非常簡單,如果將取進一步簡化,整個實現差不多相當於:
const path = require('path');
const ngPackage = require('ng-packagr');
const target = path.resolve(__dirname, './projects/<libary name>');
ngPackage
.ngPackagr()
.forProject(path.resolve(target, `ng-package.prod.json`))
.withTsConfig(path.resolve(target, 'tsconfig.lib.json'))
.build()
.then(() => {
// 構建完成後乾點事
});
將上面的程式碼放到 ./build.js
,執行:
node scripts/build.js
其結果完成是等價。
build()
返回的是一個 Promise
物件,意味著可以確保構建開始前和結束後做一點額外的事。
總結
ng-packagr 極大簡化 Angular 類庫被打包出一個 APF 規範建議,雖然它以 ng-
開頭,但本質上並不一定非要在 Angular 中運用,也可以使用在 React、VUE。