Day 6,我把 node_modules 暫時移走,再執行:
npm run dev
結果:
sh: vite: command not found
重新執行:
npm install
npm 顯示:
added 25 packages
node_modules 回來了,Vite 也能重新啟動。
package.json明明沒有寫 25 個套件,這些東西是從哪裡來的?
例如:
{
"dependencies": {
"react": "^19.2.8"
},
"devDependencies": {
"vite": "^8.2.2"
}
}
直覺會這樣以為xd:
node_modules/
├── react/
└── vite/
但套件自己也會依賴其他套件。
所以實際上比較像:
我的專案
├── React
└── Vite
├── 套件 A
├── 套件 B
│ └── 套件 C
└── 套件 D
React、Vite 自己也都是 package,也可以在自己的 package.json 裡宣告 dependencies。
所以 npm 不只處理我們直接指定的套件,也會一路處理它們需要的相依套件。
原來不是 npm 偷塞東西,是我們裝的東西自己還有東西要帶 XD
node_modules 到底是什麼?我現在會把它理解成:
package.json
↓
我宣告專案需要什麼
↓
npm 解析相依關係
↓
node_modules
↓
真正安裝出來的套件內容
所以 node_modules 裡不只會有直接依賴,也可能包含套件的依賴。
因為 node_modules 比較像「安裝結果」,不是唯一的依賴資料來源。
專案裡還有:
package.json
package-lock.json
可以這樣分:
package.json
↓
專案需要哪些套件、接受哪些版本範圍
package-lock.json
↓
實際解析出的 dependency tree 與精確版本
node_modules
↓
目前真的安裝在電腦上的套件
所以只要重建需要的資訊還在,就可以再跑:
npm install
把 node_modules 建回來。
package.json 不就好了?也不完全是。
像:
"react": "^19.0.0"
這種寫法代表的是一個可接受版本範圍,不一定永遠只會是同一個精確版本。
package-lock.json 的重要作用之一,就是把實際解析出的 dependency tree 和版本記錄下來,讓之後重裝時可以更接近原本那一套環境。
我現在會把三者記成:
package.json
「我要什麼?」
package-lock.json
「這次實際裝哪一套?」
node_modules
「真的裝出來的內容」
node_modules?因為它可以靠:
package.json
+
package-lock.json
+
npm install
重新建立。
而且 node_modules 檔案很多、體積也可能很大,所以通常沒有必要整包放進 Git。
常見專案會是:
package.json ✅
package-lock.json ✅
src/ ✅
node_modules/ ❌
這也解釋了為什麼從 GitHub clone 一個 JavaScript 專案後,常常第一件事情是:
npm install
因為 clone 下來的是「專案程式碼 + 重建依賴需要的資料」,不是別人電腦裡已經安裝好的整包 node_modules。
Git clone
↓
拿到 package.json、package-lock.json、src
↓
通常沒有 node_modules
↓
npm install
↓
建立 node_modules
↓
建立 node_modules/.bin/vite
↓
npm run dev
↓
找到 vite
↓
Vite 啟動
npm install
npm run dev
現在知道這兩行中間其實發生了一整條事情了吧@@
1. 為什麼 package.json 只寫幾個套件,node_modules 裡卻可能有更多 packages?
2. 為什麼刪掉 node_modules 後,npm install 還有辦法重新建立?
3. package.json、package-lock.json、node_modules 各自扮演什麼角色?
4. 為什麼 Git 通常不保存 node_modules?