1. 引言
现代前端开发早已不是「一个 HTML 文件 + 几个 script 标签」的时代了。一个典型的前端项目会用到:
- 各种第三方库(React、Vue、Lodash……)
- 构建工具(Vite、Webpack……)
- 代码检查与格式化(ESLint、Prettier……)
- 测试框架(Vitest、Playwright……)
管理这些需要一套工具链。本文将带你梳理前端工程化的核心概念和工具。
2. package.json:项目的「说明书」
2.1 它是什么?
package.json 是每个 Node.js / 前端项目的元数据文件,记录了项目的基本信息、依赖关系、脚本命令等。你可以把它理解为项目的身份证 + 说明书。
2.2 一个典型的 package.json
{ "name": "my-awesome-app", "version": "1.0.0", "private": true, "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "vite": "^8.0.0", "@vitejs/plugin-react": "^5.0.0", "typescript": "^5.8.0" }}2.3 关键字段解读
| 字段 | 作用 |
|---|---|
name | 项目名称(如果要发布到 npm,必须唯一) |
version | 项目版本号 |
private: true | 设为 true 防止意外发布到 npm |
type | "module" 启用 ES Module(import/export),默认是 CommonJS(require) |
scripts | 定义项目命令别名 |
dependencies | 生产依赖,线上运行时需要的包 |
devDependencies | 开发依赖,只在本地开发时需要的包 |
peerDependencies | 对等依赖,要求宿主提供指定版本的包 |
3. dependencies 和 devDependencies
3.1 区别
生产依赖(dependencies): → 用户访问你的网站时必须加载的代码 → 例如:react, vue, lodash, axios
开发依赖(devDependencies): → 只在开发阶段发挥作用,打包后不进入最终产物 → 例如:vite, webpack, typescript, eslint, prettier, vitest3.2 安装命令
# 安装为生产依赖(默认)npm install react
# 安装为开发依赖npm install -D vite typescript
# 等价写法npm install --save-dev vite3.3 为什么需要区分?
| 场景 | 不区分 | 区分 |
|---|---|---|
npm install | 安装全部 | 安装全部 |
npm install --production | 安装全部 | 只安装 dependencies |
| 最终打包 | 可能误引入测试工具 | Tree shaking 更干净 |
在生产环境(CI/CD 或 Docker)中,用 npm install --production 可以显著减少安装时间和磁盘占用。
4. 版本号规则:SemVer
4.1 语义化版本
npm 采用语义化版本(Semantic Versioning,简称 SemVer),格式为:
主版本号.次版本号.修订号 MAJOR . MINOR . PATCH| 数字 | 含义 | 何时递增 |
|---|---|---|
| MAJOR | 主版本 | 不兼容的 API 修改(破坏性变更) |
| MINOR | 次版本 | 向后兼容的新功能 |
| PATCH | 修订号 | 向后兼容的 bug 修复 |
示例:3.2.1 = 主版本 3,次版本 2,修订号 1。
4.2 版本范围符号
package.json 里的版本号前面通常带有符号:
| 写法 | 含义 | 允许范围 |
|---|---|---|
^19.0.0 | 兼容补丁和小版本 | >=19.0.0 <20.0.0 |
~19.0.0 | 只兼容补丁 | >=19.0.0 <19.1.0 |
19.0.0 | 精确版本 | 只允许 19.0.0 |
* | 任意版本 | 最新的 |
>=19.0.0 | 大于等于 | ≥19.0.0 |
# 具体例子^1.2.3 → >=1.2.3 <2.0.0 # 推荐,允许新功能~1.2.3 → >=1.2.3 <1.3.0 # 保守,只允许修复1.2.3 → 1.2.3 only # 完全锁死建议:日常开发使用
^(caret),让项目自动接收 bug 修复和新功能。关键库可用精确版本。
5. npm install 与 node_modules
5.1 npm install 做了什么?
npm install当你执行这条命令时,npm 会:
- 读取
package.json中的dependencies和devDependencies - 解析依赖树,找出所有依赖的依赖(传递依赖)
- 将每个包的代码下载到
node_modules/目录 - 生成或更新
package-lock.json
5.2 node_modules 的「黑洞」
node_modules 目录以巨大著称。一个中等项目可能包含数百 MB、上万个文件:
node_modules/├── react/├── react-dom/├── vite/├── typescript/├── ...(数百个包)重要:node_modules 不应该提交到 Git。.gitignore 中应包含:
node_modules/团队成员 clone 项目后,运行 npm install 即可重新生成。
6. lock 文件:锁死确切版本
6.1 为什么需要 lock 文件?
package.json 使用 ^1.2.3 这样的范围版本,意味着不同时间 npm install 可能装到不同版本:
3月1日安装:react ^19.0.0 → 19.0.05月1日安装:react ^19.0.0 → 19.2.3 ← 不一样!这会导致经典的「在我机器上能跑啊」问题。
6.2 lock 文件的作用
| 工具 | lock 文件 | 特点 |
|---|---|---|
| npm | package-lock.json | JSON 格式,记录所有包的精确版本和下载地址 |
| yarn | yarn.lock | 类 YAML 格式 |
| pnpm | pnpm-lock.yaml | YAML 格式 |
lock 文件记录了每一层依赖的确切版本,确保团队成员和 CI/CD 安装的依赖完全一致。
// package-lock.json 片段"node_modules/react": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-..."}lock 文件必须提交到 Git。 它是确保环境一致性的关键。
7. npm、yarn、pnpm:三大包管理器
7.1 发展历程
npm (2010) → 第一个 Node.js 包管理器,随 Node.js 一起安装
yarn (2016) → Facebook 出品,解决 npm 速度慢和不一致问题
pnpm (2017) → 磁盘效率最优,严格处理依赖7.2 核心对比
| 特性 | npm | yarn | pnpm |
|---|---|---|---|
| 安装速度 | 中等 | 快 | 最快 |
| 磁盘效率 | 低(每个项目独立拷贝) | 低(同 npm) | 高(全局存储 + 硬链接) |
| 幽灵依赖 | 允许 | 允许 | 禁止(严格模式) |
| lock 文件 | package-lock.json | yarn.lock | pnpm-lock.yaml |
| Monorepo 支持 | Workspaces | Workspaces | 原生一流支持 |
7.3 「幽灵依赖」问题
npm 和 yarn Classic 会将所有依赖扁平化放在 node_modules 顶层。这使得你可以 require('express') 即使你没有在 package.json 中声明它(如果某个包依赖了 express)。
pnpm 使用严格的嵌套依赖,你只能访问自己在 package.json 中声明的依赖,在写代码时就暴露了遗漏的依赖。
7.4 如何选择?
| 场景 | 推荐 |
|---|---|
| 个人项目、快速原型 | npm(零配置) |
| 团队协作、中大型项目 | pnpm(快、省磁盘、严格) |
| Monorepo | pnpm(原生支持最好) |
8. scripts 与 npm run:给命令起别名
8.1 scripts 是什么?
package.json 中的 scripts 字段允许你给复杂的命令起短别名:
{ "scripts": { "dev": "vite --host 0.0.0.0 --port 3000", "build": "tsc && vite build", "lint": "eslint . --ext .ts,.tsx", "format": "prettier --write .", "test": "vitest run", "test:watch": "vitest" }}使用时:
npm run dev # 相当于执行 vite --host 0.0.0.0 --port 3000npm run build # 相当于执行 tsc && vite buildnpm run lint # 相当于执行 eslint . --ext .ts,.tsx8.2 特殊脚本
有些脚本名可以直接省略 run:
npm start # 等同于 npm run startnpm test # 等同于 npm run testnpm stop # 等同于 npm run stopnpm restart # 等同于 npm run restart8.3 pre 和 post 钩子
npm 有内置的生命周期钩子:
{ "scripts": { "prebuild": "npm run clean", // build 之前运行 "build": "vite build", // 主命令 "postbuild": "npm run analyze" // build 之后运行 }}执行 npm run build 时,npm 按 prebuild → build → postbuild 顺序执行。
9. Vite 和 Webpack:构建工具
9.1 为什么需要构建工具?
浏览器无法直接运行 .vue、.tsx、.scss 文件,也不支持 import 路径别名。构建工具的作用是把开发代码转换成浏览器能运行的代码:
源文件(.tsx / .vue / .scss) ↓ 构建工具(Vite / Webpack)最终产物(.js / .css / .html)9.2 Webpack
Webpack 曾是前端构建的绝对王者(2014-2022)。它的核心思想:一切皆模块,通过 Loader 和 Plugin 处理所有文件。
module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.tsx?$/, use: 'ts-loader' }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './index.html' }), ],};| 优势 | 劣势 |
|---|---|
| 生态极其丰富 | 配置复杂(Webpack 配置工程师) |
| 插件体系非常成熟 | 构建速度慢(JS 单线程) |
| 存量项目众多 | 开发服务器启动慢 |
9.3 Vite
Vite(法语「快速」)由 Vue 作者尤雨溪打造,2022 年后迅速成为主流:
import { defineConfig } from 'vite';import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()], server: { port: 3000 },});Vite 与 Webpack 的核心区别:
| 维度 | Webpack | Vite |
|---|---|---|
| 开发模式 | 打包所有模块再启动 → 慢 | 利用浏览器原生 ESM,按需编译 → 快 |
| 底层引擎 | JavaScript (Node.js) | esbuild (Go) + Rolldown (Rust) |
| 启动速度 | 几十秒(大中型项目) | 毫秒级 |
| HMR 热更新 | 随模块数量增加而变慢 | 极快,与模块数量无关 |
| 配置复杂度 | 高(需理解 Loader/Plugin) | 低(开箱即用) |
| 生产构建 | Webpack 自身 | Rolldown(Rust,速度碾压) |
9.4 如何选择?
| 场景 | 推荐 |
|---|---|
| 新项目 | Vite(更快、更简单、更现代) |
| 老项目维护 | 保持现有 Webpack 配置不变 |
| 需要兼容老旧浏览器 | Webpack(生态更成熟) |
| 需要深度定制构建 | Webpack(插件最丰富) |
10. 小结
前端工具链看起来很复杂,但每个工具都有明确的定位:
| 工具/概念 | 一句话定位 |
|---|---|
package.json | 项目的身份证和说明书 |
dependencies / devDependencies | 运行时依赖 vs 开发时依赖 |
| SemVer | 主.次.修 标准化版本管理 |
node_modules | 依赖的实际代码存放处 |
| lock 文件 | 锁死依赖的确切版本,确保环境一致 |
| npm / yarn / pnpm | 安装依赖、管理版本的工具 |
scripts | 给复杂命令起别名 |
| Vite / Webpack | 把开发代码编译成浏览器能运行的产物 |
掌握这些工具链,你就能看懂任何一个前端项目的「骨架」,不再被 package.json 里密密麻麻的字段吓到。前端工程化,说到底就是在解决一个问题:让代码从「能写」到「能跑」到「能维护」。