Tại sao sử dụng phụ thuộc ngang hàng trong npm cho plugin?


217

Ví dụ, tại sao một plugin Grunt xác định sự phụ thuộc của nó vào grunt là " phụ thuộc ngang hàng "?

Tại sao plugin không thể có Grunt làm phụ thuộc riêng của nó trong grunt-plug / node_modules ?

Phụ thuộc ngang hàng được mô tả ở đây: https://nodejs.org/en/blog/npm/peer-dependencies/

Nhưng tôi không thực sự hiểu nó.

Thí dụ

Tôi đang làm việc với AppGyver Steroids tại thời điểm này sử dụng các tác vụ Grunt để xây dựng các tệp nguồn của tôi thành một thư mục / dist / để được phục vụ trên một thiết bị cục bộ. Tôi khá mới ở npm và grunt vì vậy tôi muốn hiểu đầy đủ những gì đang diễn ra.

Cho đến nay tôi nhận được điều này:

[rootfolder] /package.json nói với npm, nó phụ thuộc vào grunt-steroidsgói npm để phát triển:

  "devDependencies": {
    "grunt-steroids": "0.x"
  },

Được chứ. Chạy cài đặt npm trong [rootfolder] phát hiện sự phụ thuộc và cài đặt grunt-steroid trong [rootfolder] / node_modules / grunt-steroids .

Npm sau đó đọc [rootfolder] /node_modules/grunt-steroids/package.json để nó có thể cài đặt các grunt-steroidsphụ thuộc riêng :.

"devDependencies": {
    "grunt-contrib-nodeunit": "0.3.0",
    "grunt": "0.4.4"
  },
"dependencies": {
    "wrench": "1.5.4",
    "chalk": "0.3.0",
    "xml2js": "0.4.1",
    "lodash": "2.4.1"
  },
"peerDependencies": {
    "grunt": "0.4.4",
    "grunt-contrib-copy": "0.5.0",
    "grunt-contrib-clean": "0.5.0",
    "grunt-contrib-concat": "0.4.0",
    "grunt-contrib-coffee": "0.10.1",
    "grunt-contrib-sass": "0.7.3",
    "grunt-extend-config": "0.9.2"
  },

Các gói " phụ thuộc " được cài đặt vào [rootfolder] / node_modules / grunt-steroids / node_modules , điều này hợp lý với tôi.

" DevDependencies " chưa được cài đặt, mà tôi chắc chắn được kiểm soát bởi npm phát hiện tôi chỉ đang cố gắng sử dụng grunt-steroidsvà không phát triển trên đó.

Nhưng sau đó chúng ta có "đồng đẳng ".

Chúng được cài đặt trong [rootfolder] / node_modules và tôi không hiểu tại sao không có [rootfolder] / node_modules / grunt-steroids / node_modules để tránh xung đột với các plugin khác (hoặc bất cứ điều gì)?

Câu trả lời:


420

TL; DR: [1] peerDependencies dành cho các phụ thuộc được tiếp xúc với (và dự kiến ​​sẽ được sử dụng bởi) mã tiêu thụ, trái ngược với các phụ thuộc "riêng tư" không bị lộ và chỉ là một chi tiết triển khai.

Các vấn đề phụ thuộc ngang hàng giải quyết

Hệ thống mô-đun của NPM là phân cấp. Một lợi thế lớn cho các kịch bản đơn giản hơn là khi bạn cài đặt gói npm, gói đó mang phụ thuộc riêng của nó với nó để nó sẽ hoạt động tốt.

Nhưng vấn đề phát sinh khi:

  • Cả dự án của bạn và một số mô-đun bạn đang sử dụng đều phụ thuộc vào mô-đun khác.
  • Ba mô-đun phải nói chuyện với nhau.

Trong ví dụ

Giả sử bạn đang xây dựng YourCoolProjectvà bạn đang sử dụng cả hai JacksModule 1.0JillsModule 2.0. Và giả sử điều đó JacksModulecũng phụ thuộc vào JillsModule, nhưng vào một phiên bản khác, nói 1.0. Miễn là 2 phiên bản đó không đáp ứng, không có vấn đề gì. Thực tế JacksModulelà sử dụng JillsModulebên dưới bề mặt chỉ là một chi tiết thực hiện. Chúng tôi đang gói JillsModulehai lần, nhưng đó là một cái giá nhỏ phải trả khi chúng tôi nhận được phần mềm ổn định.

Nhưng bây giờ nếu như JacksModulephơi bày sự phụ thuộc của nó vào JillsModulemột cách nào đó. Nó chấp nhận một ví dụ của JillsClassví dụ ... Điều gì xảy ra khi chúng ta tạo một new JillsClassphiên bản sử dụng 2.0của thư viện và chuyển nó sang jacksFunction? Tất cả địa ngục sẽ vỡ ra! Những điều đơn giản như thế jillsObject instanceof JillsClasssẽ bất ngờ quay trở lại falsejillsObjectthực sự là một thể hiện của một JillsClass , các 2.0phiên bản.

Làm thế nào phụ thuộc ngang hàng giải quyết điều này

Họ nói với npm

Tôi cần gói này, nhưng tôi cần phiên bản là một phần của dự án, không phải là phiên bản riêng tư cho mô-đun của tôi.

Khi npm thấy rằng gói của bạn đang được cài đặt vào một dự án không có sự phụ thuộc đó hoặc có phiên bản không tương thích của nó, nó sẽ cảnh báo người dùng trong quá trình cài đặt.

Khi nào bạn nên sử dụng phụ thuộc ngang hàng?

  • Khi bạn đang xây dựng một thư viện để được sử dụng bởi các dự án khác,
  • Thư viện này đang sử dụng một số thư viện khác
  • Bạn mong đợi / cần người dùng làm việc với thư viện khác đó

Các kịch bản phổ biến là các plugin cho các khung lớn hơn. Hãy nghĩ về những thứ như Gulp, Grunt, Babel, Mocha, v.v. Nếu bạn viết một plugin Gulp, bạn muốn plugin đó hoạt động với cùng một Gulp mà dự án của người dùng đang sử dụng, không phải với phiên bản Gulp riêng của bạn.


Chú thích

  1. Quá lâu; đã không đọc. Được sử dụng để chỉ một bản tóm tắt ngắn cho một văn bản mà người ta cho là quá dài.

2
Một điều quan trọng tôi nhận thấy và không được nói ở bất cứ đâu, khi chúng tôi xây dựng một plugin, chúng ta có nên có một bản sao phụ thuộc gói, cho các phụ thuộc ngang hàng không? Trong ví dụ về OP, chúng ta có thể thấy "grunt": "0.4.4"cả về devDependencies và peDependencies, và nó có ý nghĩa với tôi để có một bản sao ở đó, bởi vì nó có nghĩa là tôi cần gruntgói đó cho mục đích sử dụng của riêng tôi, nhưng cũng là người dùng của tôi thư viện có thể sử dụng phiên bản của riêng họ, miễn là nó tôn trọng khóa phiên bản ngang hàng. Đúng không? Hoặc là ví dụ OP là một ví dụ rất xấu?
Vadorequest

4
Tôi có thể tưởng tượng mọi người tạo plugin Grunt là người hâm mộ của Grunt :) Vì vậy, việc họ sử dụng Grunt cho quá trình xây dựng plugin của họ là điều tự nhiên .... Nhưng tại sao họ lại muốn khóa phiên bản Grunt mà plugin của họ hoạt động với quá trình xây dựng họ sử dụng để tạo ra nó? Thêm nó như là một phụ thuộc dev cho phép họ tách rời điều này. Về cơ bản có 2 giai đoạn: thời gian xây dựng và thời gian chạy. Dev phụ thuộc là cần thiết trong thời gian xây dựng. Phụ thuộc thường xuyên và ngang hàng là cần thiết trong thời gian chạy. Tất nhiên với sự phụ thuộc của sự phụ thuộc, mọi thứ trở nên khó hiểu nhanh :)
Stijn de Witt

1
Cảm ơn bạn cho câu trả lời này! Chỉ cần làm rõ, trong ví dụ của bạn, nếu JacksModulephụ thuộc vào JillsModule ^1.0.0với JillsModulelà một sự phụ thuộc ngang hàng của JacksModuleYourCoolProjectđang sử dụng JacksModuleJillsModule ^2.0.0, chúng tôi sẽ nhận được cảnh báo sự phụ thuộc ngang bởi NPM, sẽ tư vấn cho chúng tôi để cài đặt JillsModule ^1.0.0là tốt. Nhưng chuyện gì xảy ra sau đó? YourCoolProjectBây giờ sẽ có hai phiên bản JillsModulenhập khẩu thông qua import jillsModule from "..."? Và làm thế nào để tôi nhớ rằng khi tôi sử dụng JacksModuletôi cần phải vượt qua nó như một ví dụ JillsModule v1.0.0?
tonix

1
@tonix Vâng, nó thực sự sẽ là một vấn đề mà bạn có một phiên bản không tương thích. ngang hàng không giải quyết được điều đó. Nhưng nó giúp làm cho vấn đề rõ ràng. Bởi vì nó sẽ hiển thị rõ ràng phiên bản không khớp thay vì sử dụng hai phiên bản một cách im lặng. Nhà phát triển ứng dụng đang chọn thư viện sẽ phải tìm giải pháp.
Stijn de Witt

2
@tonix Hoặc tùy chọn thứ ba: sao chép JacksModulerepo, nâng cấp nó để phụ thuộc JillsModule ^2.0.0và cung cấp PR cho người duy trì dự án. Nó có thể giúp gửi một lỗi trước tiên nói rằng sự phụ thuộc này đã lỗi thời và bạn muốn giúp cập nhật nó. Nếu bạn thực hiện một PR tốt, hầu hết những người bảo trì thư viện sẽ hợp nhất nó và cảm ơn bạn vì điều đó. Nếu người bảo trì không phản hồi, bạn có thể xuất bản ngã ba của mình thành NPM được đặt tên theo tên của bạn và thay vào đó sử dụng ngã ba của bạn. Trong mọi cách, có giải pháp nhưng peerDependencieskhông tự giải quyết được.
Stijn de Witt

26

Tôi muốn giới thiệu bạn đọc lại bài viết trước. Hơi khó hiểu một chút nhưng ví dụ với winston-mail cho bạn thấy câu trả lời tại sao:

Ví dụ: hãy giả vờ rằng nó winston-mail@0.2.3được chỉ định "winston": "0.5.x"trong "dependencies"đối tượng của nó bởi vì đó là phiên bản mới nhất mà nó đã được thử nghiệm. Là một nhà phát triển ứng dụng, bạn muốn những thứ mới nhất và tuyệt vời nhất, vì vậy bạn tìm kiếm các phiên bản mới nhất winstonvà của winston-mailvà đặt chúng trong gói.json của bạn như

{
  "dependencies": {  
    "winston": "0.6.2",  
    "winston-mail": "0.2.3"  
  }  
}

Nhưng bây giờ, chạy npm cài đặt kết quả trong biểu đồ phụ thuộc không mong muốn của

├── winston@0.6.2  
└─┬ winston-mail@0.2.3                
  └── winston@0.5.11

Trong trường hợp này, có thể có nhiều phiên bản của gói sẽ gây ra một số vấn đề. Các phụ thuộc ngang hàng cho phép các nhà phát triển npm đảm bảo rằng người dùng có mô-đun cụ thể (trong thư mục gốc). Nhưng bạn đã đúng với quan điểm mô tả một phiên bản cụ thể của gói sẽ dẫn đến các vấn đề với các gói khác sử dụng các phiên bản khác. Vấn đề này liên quan đến các nhà phát triển npm, như các bài báo nêu

Một lời khuyên : các yêu cầu phụ thuộc ngang hàng, không giống như các yêu cầu đối với các phụ thuộc thông thường, nên được khoan dung . Bạn không nên khóa phụ thuộc ngang hàng của mình xuống các phiên bản vá cụ thể.

Do đó, các nhà phát triển nên tuân theo semver để xác định ngang hàng. Bạn nên mở một sự cố cho gói grunt-steroid trên GitHub ...


1
Bạn nói vậy multiple versions of a package which would cause some issuesnhưng đó không phải là toàn bộ quan điểm của một người quản lý gói sao? Họ thậm chí còn thảo luận thêm về điều này trong cùng một bài viết trong đó có 2 phiên bản của cùng một gói trong dự án: một phiên bản được cung cấp bởi nhà phát triển và một do thư viện bên thứ 3 cung cấp.
Adam Beck

1
Tôi nghĩ rằng tôi hiểu quan điểm của sự phụ thuộc ngang hàng nhưng trong winstonví dụ bây giờ tôi không thể sử dụng winston-mailthư viện vì phiên bản của tôi không phù hợp với sự phụ thuộc ngang hàng? Tôi thà rằng việc hạ cấp tạm thời từ thư viện mới nhất và lớn nhất cho thư viện 1 còn hơn là không thể sử dụng nó.
Adam Beck

1
đối với nhận xét đầu tiên của bạn, theo như tôi hiểu và sử dụng nó, thì phải thực hiện kiểm tra, ví dụ: nếu bạn có gói đã được bạn kiểm tra cho gói bên thứ 3 cụ thể, bạn không thể chắc chắn rằng nếu có thay đổi phụ thuộc của bạn (sửa lỗi, cập nhật tính năng chính) mà gói của bạn sẽ hoạt động. Do đó, bạn có thể chỉ định một phiên bản plugin cụ thể và được lưu với các thử nghiệm của mình.
Fer đến

1
Về nhận xét thứ hai của bạn: đó là lý do tại sao họ nói trong các tài liệu rằng các nhà phát triển nên khoan dung với các phụ thuộc gói của họ và nên sử dụng semver, ví dụ thay vì "0.2.1", "~ 0.2.1" -> cho phép "0.2.x" nhưng không phải "0.3.x" hoặc "> = 0.2.1" -> mọi thứ từ "0.2.x" đến "1.x" hoặc "x.2.". .. (nhưng không thực sự thích hợp hơn cho gói npm sẽ đi với ~
Fer đến

15

peerDependencies giải thích với ví dụ đơn giản nhất có thể:

{
  "name": "myPackage",
  "dependencies": {
    "foo": "^4.0.0",
    "react": "^15.0.0"
  }
}


{
  "name": "foo"
  "peerDependencies": {
    "react": "^16.0.0"
  }
}

chạy cài đặt npm trong myPackage sẽ gây ra lỗi vì nó đang cố cài đặt phiên bản React ^15.0.0foochỉ tương thích với React ^16.0.0.

ngang hàng KHÔNG được cài đặt.


Tại sao không đặt phản ứng 16 như một dep bên trong foo? theo cách đó cả 15 và 16 đều có thể sử dụng được và foo có thể sử dụng 16 và mypackage có thể sử dụng 15 không?
nitinsh99

React là một khung được khởi động trong thời gian chạy, để cả React 15 và React 16 tồn tại trên cùng một trang, bạn sẽ cần cả hai để được tăng cường đồng thời, điều này cực kỳ nặng nề và gây khó khăn cho người dùng cuối. Nếu foohoạt động với cả React 15 và React 16 thì nó có thể liệt kê ngang hàng của nó là >=15 < 17.
Jens Bodal

nitinsh99 câu trả lời của tôi là giải thích mục đích của ngang hàng với ví dụ đơn giản nhất có thể, chứ không phải làm thế nào để thoát khỏi lỗi được ném bởi ngang hàng
Christopher Tokar

@ nitinsh99 thêm phản ứng bên trong gói phụ thuộc sẽ cung cấp vấn đề như Móc - Nhiều phản ứng trong một gói
Masood
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.