เหตุใดจึงต้องใช้การพึ่งพาเพื่อนใน 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.