使用 ng-packagr 打包 Angular
為了讓 Angular 類庫應用範圍更自由,Angular 提出一套打包格式建議名曰:ofollow,noindex">Angular Package Format ,包括 FESM2015、FESM5、UMD、ESM2015、ESM5、ES2015 格式,不同格式可以在不同的環境(Angular Cli、Webpack、SystemJS等)中使用。
傳統方式需要對這些格式逐一打包,一個示例 打包指令碼寫法。這種寫法只能針對不同專案的配置,而且除非你瞭解這些格式的本質否則很難維護;後來社群根據 APF 規範實現了類庫ng-packagr ,通過簡單的配置可以將你的類庫打包成 APF 規範格式。
至 V6 以後 Angular Cli 也基於 ng-packagr 實現了另一個@angular-devkit/build-ng-packagr 應用構建器。
如何使用
既然 ng-packagr 被 Angular Cli 內建,這讓我們進一步簡化了生產一個 APF 規範格式的類庫的成本。在 Angualr Cli 裡使用ng g library
來建立一個類庫模板,例如在一個新的 Angular 應用裡執行:
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 格式,因此可以通過訪問ng-package.schema.json
瞭解所有細節,以下描述一些重點項。
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。