เหตุใดจึงต้องใช้การพึ่งพาเพื่อนใน npm สำหรับปลั๊กอิน?


218

ยกตัวอย่างเช่นทำไมปลั๊กอิน Grunt จึงกำหนดการพึ่งพา Grunt เป็น " peer dependencies "

ทำไมถึงไม่สามารถปลั๊กอินเพียงแค่มีฮึดฮัดเป็นพึ่งพาของตัวเองในแสมปลั๊ก / node_modules ?

การพึ่งพาของเพื่อนมีการอธิบายไว้ที่นี่: https://nodejs.org/en/blog/npm/peer-dependencies/

แต่ฉันไม่เข้าใจจริงๆ

ตัวอย่าง

ฉันทำงานกับ AppGyver เตียรอยด์ในขณะนี้ซึ่งใช้งาน Grunt เพื่อสร้างไฟล์ต้นฉบับของฉันลงใน / dist / โฟลเดอร์เพื่อให้บริการบนอุปกรณ์ท้องถิ่น ฉันค่อนข้างใหม่เวลา 23.00 น. และเสี้ยงฮึดฮัดแสดงความไม่พอใจดังนั้นฉันต้องการที่จะเข้าใจอย่างเต็มที่สิ่งที่เกิดขึ้น

จนถึงตอนนี้ฉันได้รับสิ่งนี้:

[rootfolder] /package.jsonบอก npm ขึ้นอยู่กับgrunt-steroidsแพ็กเกจ npm สำหรับการพัฒนา:

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

ตกลง. เล่น NPM ติดตั้งใน[RootFolder]ตรวจพบการพึ่งพาและติดตั้งฮึดฮัดเตียรอยด์ใน[RootFolder] / node_modules / ฮึดฮัดเตียรอยด์

Npm จะอ่าน[rootfolder] /node_modules/grunt-steroids/package.jsonเพื่อให้สามารถติดตั้งการgrunt-steroidsพึ่งพาของตนเองได้:

"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"
  },

"การอ้างอิง " แพคเกจติดตั้งลงใน[RootFolder] / node_modules / ฮึดฮัดเตียรอยด์ / node_modulesซึ่งเป็นตรรกะสำหรับฉัน

ไม่ได้ติดตั้ง" devDependencies " ซึ่งฉันแน่ใจว่าควบคุมโดยการตรวจสอบ npm ฉันกำลังพยายามใช้grunt-steroidsและไม่พัฒนา

แต่แล้วเราก็มี " peerDependencies "

สิ่งเหล่านี้ถูกติดตั้งใน[rootfolder] / node_modulesและฉันไม่เข้าใจว่าทำไมถึงมีและไม่อยู่ใน[rootfolder] / node_modules / grunt-steroids / node_modulesเพื่อให้เกิดความขัดแย้งกับปลั๊กอินGruntอื่น ๆ (หรืออะไรก็ตาม)

คำตอบ:


421

TL; DR: [1] peerDependenciesสำหรับการอ้างอิงที่เปิดเผย (และคาดว่าจะใช้) รหัสการบริโภคซึ่งตรงข้ามกับการอ้างอิงแบบ"ส่วนตัว"ที่ไม่ได้เปิดเผยและเป็นเพียงรายละเอียดการนำไปปฏิบัติ

ปัญหาการพึ่งพาเพื่อนแก้

ระบบโมดูลของ NPM เป็นแบบลำดับชั้น ข้อได้เปรียบที่สำคัญอย่างหนึ่งสำหรับสถานการณ์ที่ง่ายกว่าคือเมื่อคุณติดตั้งแพคเกจ npm แพคเกจนั้นจะนำการพึ่งพาของตัวเองมาใช้เพื่อที่จะทำงานได้ทันที

แต่ปัญหาเกิดขึ้นเมื่อ:

  • ทั้งโครงการของคุณและบางโมดูลที่คุณใช้ขึ้นอยู่กับโมดูลอื่น
  • ทั้งสามโมดูลจะต้องพูดคุยกัน

ในตัวอย่าง

สมมติว่าคุณกำลังสร้างYourCoolProjectและคุณกำลังใช้ทั้งสองและJacksModule 1.0 JillsModule 2.0และให้คิดว่าJacksModuleยังขึ้นอยู่กับแต่ในรุ่นที่แตกต่างกันการพูดJillsModule 1.0ตราบใดที่เวอร์ชันทั้งสองไม่เป็นไปตามนั้นก็ไม่มีปัญหา ความจริงที่JacksModuleใช้JillsModuleใต้พื้นผิวเป็นเพียงรายละเอียดการใช้งาน เรากำลังรวมกันJillsModuleสองครั้ง แต่นั่นเป็นราคาขนาดเล็กที่ต้องจ่ายเมื่อเรารับซอฟต์แวร์ที่มีความเสถียรออกจากกล่อง

แต่ตอนนี้จะเกิดอะไรขึ้นถ้ามีการJacksModuleเปิดเผยJillsModuleในบางวิธี มันรับตัวอย่างของJillsClassตัวอย่างเช่น ... จะเกิดอะไรขึ้นเมื่อเราสร้างnew JillsClassใช้รุ่น2.0ของห้องสมุดและผ่านมันไปให้jacksFunction? นรกทั้งหมดจะแตกสลาย! สิ่งที่เรียบง่ายเช่นjillsObject instanceof JillsClassจะกลับมาทันทีfalseเพราะjillsObjectเป็นอีกตัวอย่างหนึ่ง JillsClassของ2.0เวอร์ชั่น

การพึ่งพากันของเพื่อนแก้ปัญหานี้อย่างไร

พวกเขาบอก npm

ฉันต้องการแพ็กเกจนี้ แต่ฉันต้องการเวอร์ชันที่เป็นส่วนหนึ่งของโครงการไม่ใช่บางเวอร์ชั่นที่เป็นส่วนตัวสำหรับโมดูลของฉัน

เมื่อ npm เห็นว่าแพ็คเกจของคุณกำลังถูกติดตั้งในโครงการที่ไม่มีการขึ้นต่อกันนั้นหรือมีเวอร์ชันที่เข้ากันไม่ได้แพคเกจจะเตือนผู้ใช้ระหว่างกระบวนการติดตั้ง

เมื่อใดที่คุณควรใช้การพึ่งพาเพื่อน

  • เมื่อคุณกำลังสร้างห้องสมุดที่จะใช้โดยโครงการอื่นและ
  • ห้องสมุดนี้ใช้ห้องสมุดอื่นและ
  • คุณคาดหวัง / ต้องการให้ผู้ใช้ทำงานกับไลบรารีอื่นนั้นเช่นกัน

สถานการณ์ทั่วไปคือปลั๊กอินสำหรับเฟรมเวิร์กขนาดใหญ่ นึกถึงสิ่งต่าง ๆ เช่น Gulp, Grunt, Babel, Mocha เป็นต้นหากคุณเขียนปลั๊กอิน Gulp คุณต้องการให้ปลั๊กอินนั้นทำงานร่วมกับ Gulp เดียวกับที่โครงการของผู้ใช้ใช้ไม่ใช่กับ Gulp เวอร์ชันส่วนตัวของคุณเอง


คำอธิบายประกอบ

  1. นานเกินไป; ไม่ได้อ่าน ใช้เพื่อระบุการสรุปสั้น ๆ สำหรับข้อความที่ถือว่ายาวเกินไป

2
สิ่งสำคัญสิ่งหนึ่งที่ฉันสังเกตเห็นและไม่ได้กล่าวไว้ทุกที่เมื่อเรากำลังสร้างปลั๊กอินเราควรมีการพึ่งพาแพคเกจที่ซ้ำกันสำหรับการพึ่งพาเพียร์? ในตัวอย่าง OP เราจะเห็นได้ว่า"grunt": "0.4.4"มีทั้งใน devDependencies และ peerDependencies และมันสมเหตุสมผลสำหรับฉันที่จะทำซ้ำที่นั่นเพราะมันหมายถึงทั้งสองอย่างที่ฉันต้องการgruntแพ็คเกจนั้นสำหรับการใช้งานของตัวเอง แต่ยังรวมถึงผู้ใช้ของฉันด้วย ไลบรารี่สามารถใช้เวอร์ชันของตนเองได้ตราบใดที่มันเคารพการล็อคเวอร์ชัน peerDependencies ถูกต้องไหม หรือตัวอย่าง OP เป็นสิ่งที่แย่มาก?
Vadorequest

4
ฉันนึกภาพคนที่สร้างปลั๊กอิน Grunt เป็นแฟนของ Grunt :) ดังนั้นมันจึงเป็นเรื่องธรรมดาสำหรับพวกเขาที่จะใช้ Grunt สำหรับกระบวนการสร้างปลั๊กอินของพวกเขา .... แต่ทำไมพวกเขาถึงต้องการล็อครุ่น Grunt ช่วงปลั๊กอินของพวกเขาทำงาน กับกระบวนการสร้างที่พวกเขาใช้สร้างมันขึ้นมา? การเพิ่มมันเป็นการพึ่งพาผู้พัฒนาช่วยให้พวกเขาแยกส่วนนี้ออกได้ โดยทั่วไปมี 2 ขั้นตอนคือเวลาสร้างและเวลาทำงาน การพึ่งพา Dev จำเป็นในระหว่างการสร้างเวลา ปกติและการอ้างอิงเพียร์เป็นสิ่งจำเป็นที่รันไทม์ แน่นอนว่าการพึ่งพาการพึ่งพาอาศัยทุกสิ่งทำให้เกิดความสับสนอย่างรวดเร็ว :)
Stijn de Witt

1
ขอบคุณสำหรับคำตอบนี้! เพื่ออธิบายให้ชัดเจนในตัวอย่างของคุณถ้าJacksModuleขึ้นอยู่JillsModule ^1.0.0กับJillsModuleการพึ่งพาจากเพื่อนJacksModuleและ YourCoolProjectกำลังใช้JacksModuleและJillsModule ^2.0.0เราจะได้รับการเตือนเพื่อนพึ่งพาโดย NPM ซึ่งจะแนะนำให้เราติดตั้งJillsModule ^1.0.0เช่นกัน แต่จะเกิดอะไรขึ้น YourCoolProjectตอนนี้จะสามารถนำเข้าได้สองรุ่นJillsModuleผ่านimport jillsModule from "..."? และฉันจะจำได้อย่างไรว่าเมื่อฉันใช้JacksModuleฉันต้องผ่านมันเป็นตัวอย่างของJillsModule v1.0.0?
tonix

1
@tonix แน่นอนมันจะเป็นปัญหาที่คุณมีรุ่นที่เข้ากันไม่ได้ peerDependencies ไม่ได้แก้ปัญหานั้น แต่จะช่วยทำให้ปัญหาชัดเจน เพราะมันจะแสดงเวอร์ชั่นที่ไม่ตรงกันแทนการใช้สองรุ่นอย่างเงียบ ๆ นักพัฒนาแอปที่เลือกไลบรารีจะต้องค้นหาวิธีแก้ไข
Stijn de Witt

2
@tonix หรือตัวเลือกที่สาม: ลอกแบบJacksModulerepo อัปเกรดขึ้นอยู่กับJillsModule ^2.0.0และเสนอ PR ให้กับผู้ดูแลโครงการ อาจช่วยส่งข้อผิดพลาดก่อนว่าการอ้างอิงนี้ล้าสมัยและคุณต้องการช่วยปรับปรุง ถ้าคุณทำ PR ให้ดีผู้ดูแลห้องสมุดส่วนใหญ่จะรวมมันและขอบคุณสำหรับมัน หากผู้ดูแลไม่ตอบสนองคุณสามารถเผยแพร่ fork ของคุณไปยังเนมสเปซ NPM ภายใต้ชื่อของคุณและใช้ fork ของคุณแทน แต่ก็มีวิธีแก้ปัญหา แต่peerDependenciesไม่สามารถแก้ไขได้ด้วยตัวเอง
Stijn de Witt

26

ฉันอยากจะแนะนำให้คุณอ่านบทความอีกครั้งก่อน มันค่อนข้างสับสน แต่ตัวอย่างจากวินสตันเมลแสดงคำตอบว่าทำไม:

ตัวอย่างเช่นสมมติว่า winston-mail@0.2.3ระบุไว้"winston": "0.5.x"ใน"dependencies"วัตถุเนื่องจากเป็นรุ่นล่าสุดที่ถูกทดสอบ ในฐานะนักพัฒนาแอพคุณต้องการสิ่งล่าสุดและยิ่งใหญ่ที่สุดดังนั้นคุณจึงค้นหาเวอร์ชันล่าสุดwinstonและจากwinston-mailและใส่ไว้ในแพ็คเกจของคุณ

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

แต่ตอนนี้การเรียกใช้การติดตั้ง npm จะส่งผลให้มีกราฟการขึ้นต่อกันที่ไม่คาดคิด

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

ในกรณีนี้เป็นไปได้ที่จะมีแพ็คเกจหลายรุ่นซึ่งอาจทำให้เกิดปัญหาบางอย่าง การพึ่งพาแบบเพียร์ช่วยให้นักพัฒนา npm ตรวจสอบให้แน่ใจว่าผู้ใช้มีโมดูลเฉพาะ (ในโฟลเดอร์รูท) แต่คุณถูกต้องด้วยจุดที่อธิบายแพคเกจรุ่นหนึ่งที่เฉพาะเจาะจงจะนำไปสู่ปัญหาเกี่ยวกับแพคเกจอื่น ๆ ที่ใช้รุ่นอื่น ๆ ปัญหานี้เกี่ยวข้องกับนักพัฒนา npm ตามที่ระบุในบทความ

คำแนะนำหนึ่งชิ้น : ข้อกำหนดของเพียร์การพึ่งพาซึ่งแตกต่างเหล่านั้นสำหรับการอ้างอิงปกติควรจะผ่อนปรน คุณไม่ควรล็อคการพึ่งพาเพื่อนของคุณลงในเวอร์ชันแพทช์เฉพาะ

ดังนั้นผู้พัฒนาจึงควรทำตามsemverเพื่อกำหนด peerDependencies คุณควรเปิดปัญหาสำหรับแพ็คเกจ grunt-steroids บน GitHub ...


1
คุณบอกว่าmultiple versions of a package which would cause some issuesแต่นั่นไม่ใช่ทั้งหมดของผู้จัดการแพ็คเกจใช่หรือไม่ พวกเขายังพูดถึงเรื่องนี้เพิ่มเติมในบทความเดียวกันที่มี 2 รุ่นของแพคเกจเดียวกันในโครงการ: หนึ่งจัดทำโดยนักพัฒนาและอีกหนึ่งจัดทำโดยห้องสมุดบุคคลที่สาม
Adam Beck

1
ฉันคิดว่าฉันเข้าใจจุดพึ่งพาของเพียร์ แต่ในwinstonตัวอย่างฉันตอนนี้ฉันไม่สามารถใช้winston-mailไลบรารีได้เนื่องจากเวอร์ชันของฉันไม่ตรงกับการพึ่งพาเพียร์? ฉันอยากจะปรับลดรุ่นนั้นชั่วคราวจากล่าสุดและยิ่งใหญ่ที่สุดสำหรับห้องสมุด 1 แห่งมากกว่าที่จะไม่สามารถใช้งานได้เลย
Adam Beck

1
สำหรับความคิดเห็นแรกของคุณเท่าที่ฉันเข้าใจและใช้มันจะต้องทำกับการทดสอบเช่นถ้าคุณมีแพคเกจที่ได้รับการทดสอบโดยคุณในสำหรับแพคเกจของบุคคลที่สามเฉพาะคุณไม่สามารถแน่ใจได้ว่าหากมี การเปลี่ยนแปลงการพึ่งพาของคุณ (การแก้ไขข้อบกพร่องการอัปเดตคุณสมบัติที่สำคัญ) ที่แพ็คเกจของคุณจะใช้งานได้ ดังนั้นคุณสามารถระบุเวอร์ชันปลั๊กอินเฉพาะและบันทึกด้วยการทดสอบของคุณ
Fer ถึง

1
ในความคิดเห็นที่สองของคุณ: นั่นเป็นเหตุผลที่พวกเขาพูดในเอกสารที่นักพัฒนาซอฟต์แวร์ควรผ่อนปรนกับการพึ่งพาแพ็คเกจและควรใช้ semver เช่นแทนที่จะเป็น "0.2.1", "~ 0.2.1" -> อนุญาต "0.2.x" แต่ ไม่ใช่ "0.3.x" หรือ "> = 0.2.1" -> ทุกอย่างตั้งแต่ "0.2.x" ถึง "1.x" หรือ "x.2" .. (แต่ไม่เหมาะสำหรับแพ็คเกจ npm จะไปกับ ~
Fer ถึง

15

peerDependencies อธิบายด้วยตัวอย่างที่ง่ายที่สุดที่เป็นไปได้:

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


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

การเรียกใช้การติดตั้ง npm ใน myPackage จะทำให้เกิดข้อผิดพลาดเนื่องจากกำลังพยายามติดตั้ง React เวอร์ชัน^15.0.0AND fooซึ่งเข้ากันได้กับ React เท่านั้น^16.0.0เท่านั้น

ไม่ได้ติดตั้ง peerDependencies


ทำไมไม่เพียงแค่ใส่ปฏิกริยา 16 ให้เป็น dep ภายใน foo ด้วยวิธีนี้ทั้ง 15 และ 16 จะเป็น avaiable และ foo สามารถใช้ 16 และ mypackage สามารถใช้ 15 ได้?
nitinsh99

React เป็นเฟรมเวิร์กที่ bootstrapped ตอนรันไทม์เพื่อให้ทั้ง React 15 และ React 16 มีอยู่ในหน้าเดียวกันคุณจะต้องเพิ่มทั้งสองอย่างพร้อมกันซึ่งจะหนักมากและเป็นปัญหาสำหรับผู้ใช้ หากfooผลงานที่มีทั้งตอบสนอง 15 และตอบสนอง 16 แล้วมันจะแสดงรายการ peerDependency >=15 < 17ในฐานะที่เป็น
Jens Bodal

nitinsh99 คำตอบของฉันคือการอธิบายวัตถุประสงค์ของ peerDependencies กับตัวอย่างที่ง่ายที่สุดที่เป็นไปได้ไม่ใช่วิธีกำจัดข้อผิดพลาดที่เกิดขึ้นจาก peerDependencies
Christopher Tokar

@ nitinsh99 การเพิ่มการตอบสนองภายในการพึ่งพาแพคเกจจะให้ปัญหาเช่น hooks - การตอบสนองหลายครั้งในแพ็คเกจ
Masood
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.