npm的發(fā)展
最早期的npm
早期的npm的依賴(lài)會(huì)被嵌套安裝响鹃,也就是說(shuō):
{
"dependencies": {
"A": "^1.0",
"B": "^1.0",
"C": "^1.0"
}
}
如果我A,B,C三個(gè)包均引用了D包,但是A案训、B引用的是D@1.0.0
,而C引用的是D@2.0.0
粪糙,他們會(huì)分別安裝到自己的node_modules底下强霎。
// 項(xiàng)目的根node_modules
node_modules
A
node_modules
D@1.0.0
B
node_modules
D@1.0.0
C
node_modules
D@2.0.0
但是這樣會(huì)導(dǎo)致依賴(lài)地獄。會(huì)出現(xiàn)依賴(lài)路徑過(guò)長(zhǎng)蓉冈、以及文件被多次復(fù)制的問(wèn)題城舞!
npm3
為了解決依賴(lài)路徑過(guò)長(zhǎng)的問(wèn)題,在npm3之后寞酿,依賴(lài)就被扁平化管理了家夺。依賴(lài)被頂?shù)搅隧攲樱钱?dāng)出現(xiàn)上面的情況的時(shí)候伐弹,依賴(lài)的表現(xiàn)是怎么樣的拉馋?
這時(shí)候先安裝的包,會(huì)把他依賴(lài)的相應(yīng)版本提前惨好,后面安裝的D包如果版本跟被置頂?shù)陌姹咎?hào)不一致煌茴,會(huì)被安裝到其node_modules下。
// 項(xiàng)目的根node_modules
node_modules
A
B
C
node_modules
D@2.0.0
D(@1.0.0 )
但是這個(gè)會(huì)出現(xiàn)一個(gè)問(wèn)題日川,就是如果根據(jù)安裝的順序進(jìn)行依賴(lài)提升蔓腐,用戶在npm i
的時(shí)候,得到的結(jié)果是不確定的龄句。因?yàn)閚pm也做了相對(duì)應(yīng)的優(yōu)化回论,把引用次數(shù)多的包扁平化管理散罕,但當(dāng)兩個(gè)引用次數(shù)一樣的時(shí)候,那必然帶來(lái)的不確定性
npm5
為了解決上面的問(wèn)題傀蓉,在package.json的基礎(chǔ)上欧漱,又新增了 package-lock.json 文件。
雖然v5.0.x跟v5.1.0的版本不一樣僚害,我們無(wú)需記住這個(gè)硫椰,只需要稍微了解即可。
npm@5.0.x 里萨蚕,不管package.json怎么變靶草,
npm i
都會(huì)根據(jù)lock文件下載。npm@5.1.0版本后岳遥,
npm i
會(huì)無(wú)視package-lock.json文件奕翔,直接下載新的npm包;npm@5.4.2版本后浩蓉,如果package.json和package.lock文件不同那么派继,
npm i
時(shí)會(huì)根據(jù)package的版本進(jìn)行下載并更新package-lock;如果兩個(gè)文件相同則會(huì)根據(jù)package-lock文件下載捻艳,不管package有無(wú)更新
但是盡管這樣驾窟,他會(huì)有幽靈依賴(lài)的問(wèn)題。
幽靈依賴(lài)
幽靈依賴(lài)在npm@3.x的版本中就已經(jīng)出現(xiàn)了认轨,因?yàn)橛辛颂嵘奶匦陨鹇纾鲜隼又校m然項(xiàng)目中沒(méi)有在package.json中顯性聲明要安裝D@1.0.0嘁字,但是npm已經(jīng)將他提升到根部恩急,此時(shí)在項(xiàng)目中引用D并進(jìn)行使用是不會(huì)報(bào)錯(cuò)的。但是由于我們沒(méi)有顯性聲明纪蜒,假如一旦依賴(lài)A不再依賴(lài)D或者版本有變化那么此時(shí)install后代碼就會(huì)因?yàn)檎也坏揭蕾?lài)而報(bào)錯(cuò)V怨А!纯续!
當(dāng)然随珠,npm還有另一個(gè)問(wèn)題,就是依賴(lài)分身杆烁。比如我們A,B引用的是D@1.0.0牙丽,而C,E引用的是D@2.0.0,項(xiàng)目中D@1.0.0已經(jīng)被依賴(lài)提升到頂部了兔魂,那么C,E的node_modules種均會(huì)有 D@2.0.0 的依賴(lài)烤芦,因此他會(huì)被重復(fù)安裝。
pnpm
pnpm 號(hào)稱(chēng) performance npm析校,與npm的依賴(lài)提升和扁平化不同构罗。pnpm采取了一套新的策略:內(nèi)容尋址儲(chǔ)存铜涉;
還是使用上面的例子: 項(xiàng)目依賴(lài)了A、B遂唧、C芙代,之后A依賴(lài)D@1.0,B依賴(lài)D@2.0盖彭,而C也依賴(lài)D@1.0纹烹,使用 pnpm 安裝依賴(lài)后 node_modules 結(jié)構(gòu)如下
// 項(xiàng)目的根node_modules
node_modules
.pnpm
A@1.0.0
node_modules
A => <store>/A@1.0.0
D => ../../D@1.0.0
D@1.0.0
node_modules
D => <store>/D@1.0.0
B@1.0.0
node_modules
B => <store>/B@1.0.0
D => ../../D@2.0.0
C@1.0.0
node_modules
C => <store>/C@1.0.0
D => ../../D@1.0.0
A => .pnpm/A@1.0.0/node_modules/A
B => .pnpm/B@1.0.0/node_modules/B
C => .pnpm/C@1.0.0/node_modules/C
我們看到,pnpm擁有自己的.pnpm目錄召边,他會(huì)以平鋪的方式來(lái)存儲(chǔ)所有包铺呵,以依賴(lài)名加上版本號(hào)的名字為命名,實(shí)現(xiàn)了版本的復(fù)用隧熙。而且他不是通過(guò)拷貝機(jī)器緩存中的依賴(lài)到項(xiàng)目目錄下片挂,而是通過(guò)硬鏈接的方式,這能減少空間占用贞盯。
至于根目錄下用于項(xiàng)目使用的依賴(lài)音念,則是通過(guò)符號(hào)鏈接的方式,鏈接到它的 .pnpm 目錄下的對(duì)應(yīng)位置躏敢。
shamefully-hosit
默認(rèn)情況下闷愤,通過(guò)pnpm的node_modules你只能訪問(wèn)到在 package.json 文件中聲明的依賴(lài),只有依賴(lài)項(xiàng)才能訪問(wèn)未聲明的依賴(lài)項(xiàng)件余。你可能需要需要再.npmrc文件中聲明了 shamefully-host=true肝谭,他才會(huì)像npm平鋪的方式,我們才能使用package.json沒(méi)有顯性聲明的幽靈依賴(lài)蛾扇。
不過(guò)事實(shí)上,pnpm的嚴(yán)格模式能夠幫助我們避免一些低級(jí)錯(cuò)誤魏滚。正常情況下镀首,是不推薦使用羞恥提升的。