為什么大多數開發者都答不上這個關于 package.json 的簡單前端面試題
前端世界從來不缺“教程”:
- YouTube 教學視頻
- Udemy 課程
- Stack Overflow 回答
- 甚至現在還有 ChatGPT、DeepSeek、Gemini 這一票 AI 助手
上面這些東西,統統已經在那里躺著,隨便點開就是一堆“手把手”。
但真正把人坑哭的,往往不是“不會寫代碼”, 而是——以為自己已經懂了,然后在細節上翻了車。
這篇就是專門拆這種翻車現場的:
- 哪一步做錯了
- 現場是什么慘狀
- 后面是怎么把坑填上的
- 以及,怎么避免下一次再踩進去
尤其是對初級 / 入門前端來說,有一個事實可能會讓人有點崩潰:
一個不起眼的文件,可以悄無聲息地決定—— 項目到底是跑得飛快,還是直接躺地不起。
這個文件,就是:package.json。
package.json 到底在掌控什么?
對 Node.js、React,甚至任何一個基于 JS 的工程來說, package.json 就是整個項目的“中樞神經”:
- 所有依賴
- 所有腳本命令
- 所有構建入口
統統寫在這里。
一旦它亂了,整個項目就跟著一起垮。
有不少開發者第一次從 GitHub 把項目拉下來, 愉快地敲下:
npm start然后迎面就是一句:
Error: Cannot find module 'express'第一反應往往是:
“代碼是不是被改壞了?”
真相卻更簡單殘酷: ——package.json 里根本沒寫上 express 這條依賴。
下面這 9 個點,幾乎覆蓋了前端開發者在 package.json 上最常翻車的場景。
面試問到,一半人答不全; 項目遇到,一半人救不回來。
1. dependencies:撐起線上運行的“主食”
dependencies 這一欄,放的是:
代碼在“生產環境”真正需要的核心庫。
如果這里漏了一條,結果就是:項目壓根跑不起來。
一個正常的寫法,大致是這樣:
{
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.1.0"
}
}常見翻車方式:
- 本地
npm install 某庫,卻沒用--save - 或者手動改 package.json,卻忘了真正安裝
結果就是經典報錯:
Error: Cannot find module 'package-name'Fix:
重新安裝,并確保寫回 dependencies:
npm install express --save或者更現代一點(npm 7+ 默認會寫入):
npm install express只要記住一句話:
線上要用到的庫,全都該乖乖躺在 dependencies 下面。
2. devDependencies:只活在開發階段的“工具箱”
devDependencies 放的則是另一類東西:
- 代碼質量工具:ESLint
- 打包 / 轉譯工具:Babel
- 測試框架:Jest 等
典型結構如下:
{
"devDependencies": {
"eslint": "^8.57.0",
"jest": "^29.7.0"
}
}有些項目的生產事故就卡在這里—— 比如把 ESLint 放進了 dependencies:
- 線上服務器安裝了一堆完全沒必要的開發工具
- 構建時間肉眼可見地被拖長
- 鏡像體積也被白白撐大
Fix:
所有“只在開發期用”的東西,都建議用 --save-dev 安裝:
npm install eslint --save-dev要記住的區分很簡單:
- 跑業務邏輯、線上必須 → dependencies
- 輔助開發、跑完就下班 → devDependencies
3. scripts:一個名字寫錯,命令全廢
"scripts" 是 package.json 里最高頻被用到的區域:
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest",
"build": "react-scripts build"
}
}NPM 有一條“隱藏規則”:
npm start會自動去找"start"對應的腳本- 如果壓根沒有
"start"這一項, 那么終端就會一臉冷漠:什么都不會執行
不少新人會出于“好看”或者“自創約定”,把 start 改名:
"run": "node server.js"然后困惑地問: “為什么 npm start 不動了?”
Fix:
無論腳本再多再花哨,**"start" 一項一定要留著**。 就算只是最簡單的:
"start": "node index.js"這是 npm 世界里,最基礎、也是最經常被考察的一條共識。
4. type 字段:require 和 import 只能選一邊站
當某個項目在 package.json 里加上:
{
"type": "module"
}就等于對 Node.js 宣告:
“這是一個 ES Module 項目。”
緊接著,所有 require() 寫法會被視作“不合法”——
ReferenceError: require is not defined in ES module scope于是整個項目開始一半 import、一半 require, 到處報錯,哭都不知道從哪兒開始。
Fix 分兩種路線:
如果項目已經大量用 require:
- 暫時不要加
"type": "module" - 或者只對部分文件使用
.mjs擴展名來嘗鮮
如果準備全面擁抱 import/export:
- 把原有的
require/module.exports逐步遷移 - 修一修 Node 版本、打包工具配置
一句話:
"type": "module"不是一個“順手就能加”的裝飾字段, 而是一條語法立場的選擇題。
5. bin:把腳本變成真正的命令行工具
當一個 npm 包希望被這樣使用時:
mardinjs build就必須在 package.json 里聲明 **"bin"**:
{
"bin": {
"mardinjs": "./bin/mardin.js"
}
}比如有些庫(例如性能優化相關的工具包 mardinjs), 就是通過這種方式提供命令行能力。
常見翻車點:
- 指向的
./bin/mardin.js根本不存在 - 文件存在,卻沒有可執行權限
- 或者忘記在文件頭部聲明解釋器
結果就是用戶一安裝,立刻看到:
Error: cannot find module './bin/mardin.js'Fix:
確保路徑正確,文件真實存在。
文件頂部加上 shebang:
#!/usr/bin/env node賦予可執行權限:
chmod +x ./bin/mardin.js只要這三步走對,包就能像一條真正的 CLI 命令一樣被調用。
6. engines:被 Node 版本“反向教育”的那一刻
有些最糟糕的部署事故,表面看起來是“語法突然報錯”, 實際上是——線上 Node 版本根本帶不動本地寫好的代碼。
典型場景:
- 本地用 Node 18 寫了各種:
可選鏈 ?.
空值合并 ??
- 結果線上服務器還是 Node 14
- 一上線,就是大面積語法崩潰
這時候,"engines" 字段就該出場了:
{
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
}
}它的作用是:
- 告訴安裝者:這個項目最低需要什么版本的 Node / npm
- 某些平臺(如某些云服務、包管理工具)會在不滿足時直接報警甚至拒絕構建
還可以配合 "engineStrict": true 做更強硬的限制。
對團隊協作和長期維護來說,這是一個非常值的“提前聲明”。
7. files:決定發到 npm 上的,究竟有哪些東西
當一個包要發布到 npm 時,**"files" 字段負責決定:**
哪些目錄、哪些文件,會真正被上傳。
例如:
{
"files": ["dist", "bin"]
}很常見的一次翻車,是:
- 忘了把
dist(打包后的產物)寫進來 - 發布之后,用戶安裝到本地
- 結果下載到的是一個“只有源碼、沒有構建產物”的空殼
Fix:
- 確認構建輸出目錄(如
dist、build)確實包含在"files"里 - 或者通過
.npmignore來排除不想發出去的文件
一句話:
“源碼寫得多好不重要, 用戶真正拿到的,是
files指定的那一份。”
8. readme & 元數據:不僅是門面,也是“可維護性”的一部分
npmjs.com 上很多項目的詳情頁,看上去一片空白:
- 沒有
readme description是一句敷衍author留著默認或亂填
一個最基本的元數據結構,大致應該是這樣:
{
"name": "my-app",
"version": "1.0.0",
"description": "A simple React + Node.js project",
"author": "Your Name"
}這些字段的意義遠不止“好看”:
- 方便別人搜索和評估這個項目
- 方便團隊內部追溯“是誰寫的、做什么用的”
- 對開源項目來說,也是“對用戶負責”的基本禮節
在很多正式面試里, 面試官一看到 package.json 連最基本的信息都不完整, 印象分就已經先掉一截。
9. 當依賴樹“腐爛”的時候,最簡單那招反而最好用
有時候,項目會突然陷入一種“到處都是奇怪錯誤”的狀態:
- 明明昨天還能跑
- 今天各種莫名其妙的報錯
- 重啟、重裝、重試,依然一團亂麻
多半是因為:依賴樹已經腐爛了。
比如:
- 某個庫偷偷發了一個問題版本
- 鎖文件和 node_modules 不一致
- 反復安裝疊出了一堆幽靈依賴
而很多團隊最后的解決方案,往往出奇樸素:
rm -rf node_modules
rm package-lock.json
npm install然后,一切恢復正常。
這一刻,很多人會意識到:
依賴不是一次裝完就能永遠放心,依賴衛生(dependency hygiene)是必須長期維護的一項工程。
結語:package.json 是項目的“心跳圖”
package.json 從來不是一個“隨便寫寫的配置文件”, 而是一個項目的生命體征:
- 依賴關系
- 腳本命令
- 運行環境
- 發布內容
每一個字段, 都在悄悄決定:
這個項目是能在面試官的電腦上“一鍵跑起”, 還是在 CI、生產、用戶設備上,一次次翻車。
忽視它,它會用最殘忍的方式回擊—— 從 npm start 的第一個報錯開始。
理解它,項目的穩定性、可維護性和“面試可通過程度”, 都會肉眼可見地提高一大截。
下一步,真正麻煩的部分還在后面——
- 版本不匹配
- peerDependencies 報錯
- 經典的 “dependency hell”
這些東西,完全可以在項目剛起步的時候就被處理掉, 而不是等到 React 或 Node.js 項目已經跑到一半,才被迫返工。
想避免那些“明明代碼沒問題,卻被依賴拖下水”的場景, 從看懂、寫好、維護好一個 package.json 開始,就已經贏了半步。































