ng new app 生成的 Angular 應用,自帶11個依賴:
使用 Schematics 安裝了 library 之後的客戶 Storefront:
本地新建一個空的資料夾,在裡面執行命令列:npm i @spartacus/storefront
裡面只有一個 node_modules 資料夾,裡面包含了很多 js 檔案和 TypeScript 的 .d.ts 檔案:
這個 @Spartacus/storefront 的 package.json 裡,仍舊只有一個 tslib 的 dependencies:
但是 peerDependencies
裡,包含了不少在 Spartacus 專案原始碼 package.json 裡定義的 dependencies
:
"peerDependencies": {
"@angular/common": "^12.0.5",
"@angular/core": "^12.0.5",
"@angular/forms": "^12.0.5",
"@angular/platform-browser": "^12.0.5",
"@angular/router": "^12.0.5",
"@angular/service-worker": "^12.0.5",
"@ng-bootstrap/ng-bootstrap": "^10.0.0",
"@ng-select/ng-select": "^7.0.1",
"@ngrx/effects": "^12.1.0",
"@ngrx/router-store": "^12.1.0",
"@ngrx/store": "^12.1.0",
"@spartacus/core": "4.3.1",
"ngx-infinite-scroll": "^8.0.0",
"rxjs": "^6.6.0"
},
npm 很好地處理了子依賴關係:如果我的包依賴於 request 版本 2 和其他庫,但其他庫依賴於 request 版本 1,則生成的依賴關係圖如下所示:
這通常很棒:現在 some-other-library 擁有自己的請求 v1 副本,它可以使用它,同時不會干擾我的包的 v2 副本。
但是,有一個用例會失敗:外掛(plugin)。 外掛包旨在與另一個 host 包一起使用,即使它並不總是直接使用 host 包。 Node.js 包生態系統中已經有很多這種模式的例子:
- Grunt plugins
- Chai plugins
- LevelUP plugins
- Express middleware
- Winston transports
從本質上講,外掛旨在與主機包一起使用。 但更重要的是,它們旨在與特定版本的主機包一起使用。 例如,我的 chai-as-promised 外掛的 1.x 和 2.x 版本適用於 chai 0.5 版,而 3.x 版適用於 chai 1.x。 另一個例子是 grunt. 0.3.1 版的 grunt-contrib-stylus 可以與 grunt 0.4.0rc4 一起使用,但由於刪除了 API,在與 grunt 0.4.0rc5 一起使用時會中斷。
假設 plugin 顯式宣告瞭 host package 的版本號,即使對於確實具有這種直接依賴關係的外掛,可能是由於主機包提供了實用程式 API,在外掛的 package.json 中指定依賴項也會導致依賴關係樹包含主機包的多個副本。例如,假設 winston-mail 0.2.3 在其 dependencies 中指定了 winston: 0.5.x,因為這是對其進行測試的最新版本。
作為應用程式開發人員,使用了 winston 的最新版本 0.6, 將它們放在 package.json 中:
一旦執行 npm install
後,產生 winston 的兩份不同的版本:
這種問題的解決方案就是 peerDependencies
.
peerDependencies 使用起來非常簡單。 在編寫 plugin 時,請確定 peerDependencies 的 host package 的版本,並將其新增到 package.json 中:
{
"name": "chai-as-promised",
"peerDependencies": {
"chai": "1.x"
}
}
現在,當安裝 chai-as-promised 時,chai 包將隨之被安裝。
如果稍後嘗試安裝另一個僅適用於 0.x 版本的 Chai 的 Chai 外掛,將收到錯誤訊息。