13 分钟
全栈开发

前端工程化工具链:npm、Vite 与环境变量

认识 package.json、装包、Vite 开发服务器、ES Module 与 .env

  • 理解 package.json 和 node_modules 的作用
  • 知道 npm/pnpm 装包是干嘛的
  • 理解 Vite dev server 与打包产物
  • 了解 ES Module import/export 与环境变量
  • 知道 ESLint/Prettier 在团队里做什么
💡高中搭桥

前端这套工具链,相当于 Python 世界的 pip + requirements.txt + 一键运行脚本:package.json 就是 requirements.txt,npm install 就是 pip install,node_modules 就是你装的第三方库,Vite 就是那个「改一行代码立刻在浏览器看到」的本地运行器。

package.json 与 node_modules

每个前端项目根目录都有一个 package.json,它是项目的身份证 + 依赖清单:记录项目名、脚本命令、以及 dependencies(生产依赖)和 devDependencies(开发时才用的工具)。

示例
{
  "name": "my-app",
  "scripts": {
    "dev": "vite",          // npm run dev 启动开发服务器
    "build": "vite build"    // npm run build 打包上线
  },
  "dependencies": {
    "react": "^18.3.1"       // 运行时需要的库
  },
  "devDependencies": {
    "vite": "^5.4.0",        // 只在开发时用的构建工具
    "typescript": "^5.5.0"
  }
}

npm install 会照着 package.json 把这些库下载到 node_modules 文件夹里。这个文件夹很大、不要手动改、也不要提交到 Git(靠 package.json + lock 文件就能在任何机器上还原出一模一样的依赖)。

这里有个容易忽略但关键的文件:lock 文件(package-lock.json 或 pnpm-lock.yaml)。package.json 里写的是 ^18.3.1 这种「兼容版本」,意味着允许装 18.x 的最新版;但 ^ 号在你不同时间 install 时可能装到不同小版本,导致「我电脑上好好的,你那边就崩」。lock 文件把你实际装到的每个包的精确版本锁死,团队所有人、CI、线上都按它装出完全一致的依赖。所以 lock 文件必须提交 Git,别无视它。

npm / pnpm 装包

ℹ️常用命令

npm install(装全部依赖)、npm install react(装一个新包)、npm uninstall react(卸载)。pnpm 是更省磁盘、装得更快的替代品,用法几乎一样。包的版本号和价格信息以官方最新为准。

npm 有个著名的「黑洞」问题:它给每个项目、甚至项目里每个嵌套依赖,都完整复制一份 node_modules。结果就是一个 node_modules 动辄几百 MB,装一次等好几分钟,三个项目依赖同一个 react 就复制三遍。更隐蔽的是幽灵依赖——你没在 package.json 里声明、却能直接 import 的包,因为它被别的依赖顺带装进来了,哪天上游一升级就突然找不到。

pnpm 的思路是「全局存一份,项目里做硬链接」:所有包在全局仓库只存一份实体,各项目用硬链接指过去,磁盘占用从重复 N 份变成一份;同时它用严格的符号链接布局,你没声明的包 import 不到,从根上杜绝幽灵依赖。装得快、省空间、依赖更干净,这就是为什么现在大项目普遍转 pnpm。

Vite:开发服务器与打包

Vite 是现代前端的构建工具。开发时它启动一个本地 dev server(通常 localhost:5173),你改代码它只重新编译改动的那一小块,浏览器秒热更新;上线时它把所有代码打包、压缩成浏览器能高效加载的静态文件。

为什么 Vite 比老的 Webpack 快那么多?两个关键。第一,开发模式下它直接用浏览器原生 ES Module 加载,不再像 Webpack 那样把整个项目先打成一个大包才能跑——你只访问哪个模块,它才现场编译哪个模块,冷启动从「等几十秒打包」变成「瞬间开服」。第二,它用 esbuild(用 Go 写的预构建器)把第三方依赖提前编译一次并缓存,esbuild 比 JS 写的打包器快一个数量级。上线构建时才用 Rollup 做整包压缩优化。

ℹ️数字直觉(示例口径)

老 Webpack 冷启动一个中型项目可能要 20–40 秒才能跑起来;Vite 开发服务器通常亚秒级启动,改一行代码的热更新耗时在毫秒级。具体数字随项目规模与机器浮动,这里给的是量级感受,不是基准测试结论。

示例代码(可运行)

ES Module:import / export

前端用 ES Module 组织代码:一个文件用 export 导出,另一个文件用 import 引入。这和 Python 的 import 几乎一样。

TypeScript
// utils.ts —— 导出
export function formatName(name: string) {
  return name.trim().toUpperCase();
}
export const MAX = 100;

// App.tsx —— 导入
import { formatName, MAX } from "./utils";
console.log(formatName("  小明 "));  // 小明

环境变量、ESLint 与 Prettier

环境变量放在 .env 文件里(如 API 地址、端口),不写死在代码中,这样换环境(开发/线上)不用改代码。前端里以 VITE_ 前缀暴露给浏览器。ESLint 检查代码有没有低级错误和不规范写法,Prettier 自动统一代码风格(缩进、引号),团队协作时不用为空格吵。

🚫环境变量泄露风险

前端构建工具只把「带公开前缀」的变量打进浏览器包:Vite 要 VITE_ 开头,Next.js 要 NEXT_PUBLIC_ 开头。反过来说——凡是带这个前缀的变量,最终都会出现在浏览器下载的 JS 里,用户 F12 就能看到,绝不能塞数据库密码、私钥这类机密。真机密只能放在服务端、不带前缀、浏览器根本拿不到。新手常犯的错是把 API 密钥也加了公开前缀,等于公开贴出来。

🐍Monorepo 一句话

当项目大到要同时管理前端、后端、多个工具包时,会用 Monorepo(一个仓库放多个包)统一管理依赖和版本,那是 s13-m5-l1 的话题,现在知道有这回事即可。

选择题

package.json 里 dependencies 字段记录的是什么?

填空题填写空白处的代码
前端开发时,本地启动一个改代码自动刷新的开发服务器,这个工具叫 。

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

网络侧:DNS→TCP/TLS→HTTP 请求与响应;浏览器侧:解析 HTML 建 DOM、解析 CSS 建 CSSOM、合并成渲染树,再经过布局(Layout)、绘制(Paint)、合成(Composite)上屏。前端性能优化几乎都围绕这条链路:减小资源体积、优化关键渲染路径、避免频繁触发重排重绘。

挑战任务

读取环境变量并给默认值

简单+50 XP

前端常把端口写在环境变量里,缺省时给一个默认端口。实现 get_port():从环境变量 PORT 读取并转成整数,没有就用 8080。

读取环境变量并给默认值
1 个测试用例

课后作业

判断环境变量能不能发给浏览器

中等+15 XP

构建工具只把带公开前缀的变量打进前端 JS,用户 F12 就能看到。实现 is_public_env(name):变量名以 VITE_ 或 NEXT_PUBLIC_ 开头返回 True(会暴露给浏览器,只能放非机密),否则 False(必须留在服务端)。打印四个例子。

判断环境变量能不能发给浏览器
1 个测试用例