早期的npm(npm v2)
- 在安裝依賴的時(shí)候會(huì)將依賴放到
node_modules
文件中今妄; - 如果某個(gè)包A依賴于B,B作為間接依賴犬性,會(huì)被安裝到A的文件夾的
node_modules
中,遞歸進(jìn)行日裙,直到子依賴中不再依賴其他模塊受神;
優(yōu)點(diǎn):層級(jí)結(jié)構(gòu)明顯联四,每次安裝的目錄結(jié)構(gòu)都是相同的
存在的問題
- 如果項(xiàng)目過大,必然會(huì)形成一棵巨大的依賴樹,嵌套層級(jí)非常深排吴。
- 不同層級(jí)中存在相同的模塊,導(dǎo)致大量冗余双藕。
-
在windows系統(tǒng)中近范,文件路徑最大長(zhǎng)度為260個(gè)字符,嵌套層級(jí)過深可能導(dǎo)致不可預(yù)知的問題虱颗。
扁平結(jié)構(gòu)
為解決上面的問題 npm
在3.x
版本做了一次較大的更新畦粮,將早期的嵌套結(jié)構(gòu)改為了扁平結(jié)構(gòu):
- 安裝模塊時(shí)瞪浸,不管其是依賴還是子依賴的依賴钩蚊,優(yōu)先將其安裝在
node_modules
根目錄 - 當(dāng)安裝到相同模塊時(shí)瓤狐,判斷已安裝的模塊是否符合新的模塊的版本范圍勺美,符合則跳過,不符合則在當(dāng)前模塊的
node_modules
下安裝該模塊。
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
"base64-js": "1.0.1",
}
}
{
"name": "buffer",
"dependencies": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
}
npm 3.x
版本帶來了新的問題叭喜。
由于在執(zhí)行 npm install
的時(shí)候是按照 package.json
里依賴的順序依次解析,依賴包在 package.json
的放置順序則決定了 node_modules
的依賴結(jié)構(gòu),依賴結(jié)構(gòu)的不確定性可能會(huì)給程序帶來不可預(yù)知的問題腊嗡。
Lock文件
為了解決npm install
的不確定性問題卡者,npm 5.x
版本中新增了package-lock.json
文件底挫,安裝方式還是沿用npm 3.x
的扁平化方式。package-lock.json
的作用是鎖定依賴結(jié)構(gòu)外遇,即只要項(xiàng)目目錄下有package-lock.json
捐晶,那么每次執(zhí)行 npm install
后生成的node_modules
目錄結(jié)構(gòu)一定是完全相同的
例如谨究,我們有如下的依賴結(jié)構(gòu):
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
"base64-js": "1.0.1",
}
}
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"base64-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz",
"integrity": "sha1-aSbRsZT7xze47tUTdW3i/Np+pAg="
},
"buffer": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
"integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
},
"dependencies": {
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
}
}
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"ignore": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
"integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A=="
}
}
}
最外面的兩個(gè)屬性 name
把敢、version
同 package.json
中的 name
和 version
柏副,用于描述當(dāng)前包名稱和版本蕉饼。
dependencies
是一個(gè)對(duì)象创肥,對(duì)象和 node_modules
中的包結(jié)構(gòu)一一對(duì)應(yīng)塔猾,對(duì)象的 key
為包名稱睦擂,值為包的一些描述信息:
-
version
:包版本 —— 這個(gè)包當(dāng)前安裝在node_modules
中的版本 -
resolved
:包具體的安裝來源 -
integrity
:包hash
值,基于Subresource Integrity
來驗(yàn)證已安裝的軟件包是否被改動(dòng)過蕉毯、是否已失效 -
requires
:對(duì)應(yīng)子依賴的依賴坷澡,與子依賴的package.json
中dependencies
的依賴項(xiàng)相同着降。 -
dependencies
:結(jié)構(gòu)和外層的dependencies
結(jié)構(gòu)相同妆偏,存儲(chǔ)安裝在子依賴node_modules
中的依賴包愉烙。
npm的緩存機(jī)制
緩存位置: /Users/xxx/.npm
-
content-v2
目錄用于存儲(chǔ)tar包的緩存 -
index-v5
目錄用于存儲(chǔ)tar包的hash - npm在執(zhí)行安裝時(shí)省核,可以根據(jù)
package-lock.json
中存儲(chǔ)的intergiry
、version
陪毡、name
生成一個(gè)唯一的key對(duì)應(yīng)到index-v5
目錄下的緩存記錄慧耍,從而找到tar包的hash定庵,然后再根據(jù)hash取找緩存的tar包直接使用
npm的安裝流程
- 執(zhí)行
npm install
后绎晃,首先檢查和獲取npm的配置,優(yōu)先級(jí)為:
項(xiàng)目級(jí)的.npmrc文件 > 用戶級(jí)的.npmrc文件 > 全局的.npmrc文件 > npm內(nèi)置的.npmrc 文件
- 無
package-lock.json
文件:
- 從 npm 遠(yuǎn)程倉(cāng)庫(kù)(
registry
)獲取包信息 - 根據(jù)
package.json
構(gòu)建依賴樹硼砰,構(gòu)建過程:
1)構(gòu)建依賴樹時(shí),不管其是直接依賴還是子依賴的依賴括授,優(yōu)先將其放置在node_modules
根目錄。
2)當(dāng)遇到相同模塊時(shí)俭茧,判斷已放置在依賴樹的模塊版本是否符合新模塊的版本范圍,如果符合則跳過红氯,不符合則在當(dāng)前模塊的node_modules
下放置該模塊。
3)在緩存中依次查找依賴樹中的每個(gè)包,不存在緩存就從 npm 遠(yuǎn)程倉(cāng)庫(kù)下載包(下載壓縮包后绷雏,存放到 ~/.npm 目錄)早歇,將包按照依賴結(jié)構(gòu)解壓到node_mudules
4)生成 lock 文件
- 有
package-lock.json
文件:
檢查 package.json 中的依賴版本是否和 package-lock.json 中的依賴有沖突逝段。
如果沒有沖突莫瞬,直接跳過獲取包信息、構(gòu)建依賴樹過程尝哆,開始在緩存中查找包信息姥敛,后續(xù)過程相同
yarn
當(dāng)npm還處于v3時(shí)期的時(shí)候(當(dāng)時(shí)還沒有package-lock.json)玄糟,yarn作為一個(gè)新的包管理工具出現(xiàn)贰军,解決了npm的一些問題(lock词疼、緩存等問題舵盈,npm后續(xù)也進(jìn)行了優(yōu)化)
- 確定性: 用過yarn.lock 機(jī)制书释,即使是不同的安裝順序扯再,相同的依賴關(guān)系在任何環(huán)境和容器中都以相同的方式安裝。
- 模塊扁平化:將不同版本的依賴包拗小,按照一定的策略界牡,歸結(jié)為單個(gè)版本,避免冗余漾抬。
- 網(wǎng)絡(luò)性能:yarn采用了請(qǐng)求排隊(duì)的理念宿亡,類似于并發(fā)池連接,能夠更好的利用網(wǎng)絡(luò)資源纳令,同時(shí)引入了一種安裝失敗的重試機(jī)制
- 采用緩存機(jī)制挽荠,實(shí)現(xiàn)了離線模式