学会写代码只是第一步,学会「工程化地写代码」才是职业分水岭。本章覆盖 Node 基础、包管理、Git、构建原理、Vite/Webpack、代码规范、TypeScript 与 CI/CD,让你从「能写」升级到「能产出可维护的工程」。
Node 让 JS 跳出浏览器,也带来了完整的前端工具链。
// 最简单的 Node 脚本:node hello.js 运行
const fs = require("fs"); // 文件系统模块
const path = require("path"); // 路径模块
console.log("你好,Node!");
console.log(__dirname); // 当前文件所在目录
| 模块 | 作用 | 常用 API |
|---|---|---|
| fs | 文件读写 | readFile / writeFile / mkdir(都支持 Promise 版本) |
| path | 路径处理 | join / resolve / basename / extname |
| http / https | 发起 HTTP 请求 | 前端一般用 fetch 或 axios 封装 |
| os | 系统信息 | platform / cpus / totalmem |
| process | 进程信息 | env(环境变量)/ argv(命令行参数) |
// 读取文件(现代写法:Promise)
const fs = require("fs/promises");
async function readConfig() {
const data = await fs.readFile("./config.json", "utf-8");
return JSON.parse(data);
}
依赖管理是工程的命脉,版本策略与锁文件必须理解。
// 版本号格式:主版本.次版本.补丁版本(如 4.17.21)
// 主版本:不兼容的破坏性变更(升级要谨慎)
// 次版本:向后兼容的新功能
// 补丁版本:向后兼容的 bug 修复
// package.json 里的版本范围写法:
"react": "^18.2.0" // ^:允许 18.x.x(次版本内的更新),不允许 19.x
"react": "~18.2.0" // ~:只允许 18.2.x(补丁更新)
"react": "18.2.0" // 精确锁定
"react": "*" // 任意版本(千万别用)
npm init -y // 初始化项目(生成 package.json)
npm install // npm i // 安装 package.json 里的所有依赖
npm i react // 安装并写入 dependencies(生产依赖)
npm i -D typescript // -D:写入 devDependencies(开发依赖,构建产物不包含)
npm run dev // 运行 scripts 里的 dev 脚本
npm outdated // 查看依赖是否有新版本
npm audit // 检查依赖安全漏洞
pnpm:① 硬链接共享依赖,省磁盘;② 幽灵依赖隔离,杜绝「能用但没声明」的隐患;③ 安装快。团队统一用一个,别混用(混用会导致 node_modules 结构混乱)。
Git 是协作的基石,下面这些命令是生存必备。
// 拉取最新 → 建分支 → 改代码 → 提交 → 推送 → 提 PR
git pull // 拉取远程更新
git checkout -b feature/login // 新建并切换分支(命名:feature/ 前缀)
git add . // 暂存改动
git commit -m "feat: 新增登录功能" // 提交(遵循提交规范)
git push -u origin feature/login // 推送并建立关联
// 然后在远程(GitHub/GitLab)创建 Pull Request,走代码评审
| 命令 | 效果 | 适用 |
|---|---|---|
| git merge | 把分支合并进来,保留合并记录 | 默认、团队协作主流 |
| git rebase | 把提交「搬到」目标分支顶部,历史线性整洁 | 个人分支整理、保持历史清晰 |
| git cherry-pick | 挑单个提交应用到当前分支 | 紧急修复提版本 |
// 冲突解决:合并时双方改了同一处 → 打开文件找 <<<<<<< 标记,手工保留需要的部分
<<<<<<< HEAD
当前分支的内容
=======
被合并分支的内容
>>>>>>> feature/login
// 解决后:git add . && git commit(不要慌,冲突是常态)
feat: 新功能、fix: 修复、docs: 文档、refactor: 重构、perf: 性能、test: 测试、chore: 杂务。格式:类型(范围): 描述,如 fix(login): 修复密码框回车不提交。配合 husky 可以在提交前自动校验。
理解「为什么需要打包」,才能理解 Vite 和 Webpack 的设计。
| 时代 | 方案 | 问题 |
|---|---|---|
| 远古 | 多个 script 标签,全局变量 | 变量污染、依赖顺序靠人肉 |
| 第一代 | IIFE(立即执行函数)+ 全局挂载 | 手动管理依赖,还是乱 |
| 第二代 | CommonJS(Node)/ AMD(浏览器,require.js) | 浏览器无法直接用 CommonJS |
| 现代 | ES Modules(ESM) | 浏览器原生支持 + 静态分析 → 打包优化 |
require 是运行时同步加载,浏览器加载本地文件是异步的,无法同步阻塞等待——所以浏览器端模块标准必须走 ESM(import 声明是静态的,浏览器可以预先解析)。打包器的职责之一就是把 ESM/CommonJS 统一处理成浏览器能跑的形式。
| 步骤 | 做什么 | 产出 |
|---|---|---|
| ① 入口分析 | 从入口文件开始 | — |
| ② 依赖解析 | 递归解析 import/require,生成依赖图 | 依赖关系图 |
| ③ 转换编译 | loader 处理:TS→JS、JSX→JS、CSS、图片 | 转换后的模块代码 |
| ④ 合并打包 | 按规则合并成 chunk | bundle 文件 |
| ⑤ 代码分割 | 动态 import 拆成独立 chunk,按需加载 | 多个小文件 |
| ⑥ Tree Shaking | 删掉「未被使用的导出」 | 更小的体积 |
Vite 为什么快?理解它的两大核心机制。
Webpack 开发时要把所有模块打包成一个 bundle 才能启动——项目越大越慢(「启动 30 秒」梗的来源)。Vite 的思路完全不同:
存量项目大量使用 Webpack,理解它的核心概念才能维护改造。
| 概念 | 作用 | 例子 |
|---|---|---|
| Entry | 入口,打包起点 | entry: './src/main.js' |
| Output | 产物输出配置 | output: { filename: '[name].[hash].js' } |
| Loader | 转换模块内容(文件级) | babel-loader(JS)、ts-loader、css-loader、style-loader |
| Plugin | 打包全流程的扩展点(构建级) | HtmlWebpackPlugin、MiniCssExtractPlugin |
// webpack.config.js 最小示例
module.exports = {
mode: "development", // development / production
entry: "./src/index.js",
output: { path: __dirname + "/dist", filename: "bundle.js" },
module: {
rules: [
{ test: /\.js$/, use: "babel-loader", exclude: /node_modules/ },
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
],
},
plugins: [new HtmlWebpackPlugin({ template: "./index.html" })],
devServer: { port: 3000, proxy: { "/api": "http://localhost:8080" } },
};
规范不是束缚,是让团队协作零摩擦的基础设施。
| 工具 | 职责 | 典型规则 |
|---|---|---|
| ESLint | 代码质量与规范(找错误与坏味道) | 未使用变量、禁止 console、强制分号 |
| Prettier | 代码格式化(统一风格,不判断对错) | 缩进 2 空格、单引号、行宽 100 |
| EditorConfig | 编辑器级别的基础配置 | 文件编码、缩进方式(跨 IDE 统一) |
| husky + lint-staged | Git 钩子:提交前自动检查 | 提交时只检查暂存的文件,又快又准 |
// package.json 里的钩子配置(husky + lint-staged)
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,html,md}": ["prettier --write"]
}
}
// 效果:每次 git commit 前自动修复格式,坏代码进不了仓库
TS 是当前大厂前端的标配,从入门到熟练是这一节的目标。
// 基础类型标注
let age: number = 25;
let name: string = "张三";
let isDone: boolean = true;
let list: number[] = [1, 2, 3];
// 接口:定义对象结构
interface User {
id: number;
name: string;
email?: string; // ?: 可选属性
readonly createdAt: Date; // readonly:只读
}
function greet(user: User): string { // 参数类型 + 返回值类型
return `你好,${user.name}`;
}
// 泛型:类型像参数一样传进去,让函数复用且保持类型安全
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42); // T = number
const str = identity("hello"); // 自动推断 T = string
// 经典应用:API 响应包装
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
// 用一次,处处类型安全:
const res = await get<User[]>("/api/users"); // res.data 是 User[]
把「检查、构建、测试、部署」变成自动的流水线,质量与效率兼得。
// GitHub Actions 示例:.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci // 按锁文件精确安装
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
// 全部通过 → 绿色 ✅;任一失败 → 红色 ❌ 拦住合并
本章动手为主,两个项目做完即通关。
要求:① npm create vite@latest 创建项目并理解每个文件;② 接入 ESLint + Prettier + husky + lint-staged,提交不规范代码会被拦;③ 配置路径别名(@/ 指向 src);④ 配置代理解决开发期跨域;⑤ 加 TypeScript 严格模式;⑥ 配好提交规范(commitlint)。验收:随便写点代码,观察「提交时自动修复、commit 信息被校验」的效果。
用 Node 写一个 100 行左右的打包器:① 读取入口文件;② 用正则或简单的解析找到 import;③ 递归生成依赖图;④ 把模块包装成函数,生成一个可运行的 bundle.js。做完你会彻底理解「打包」到底在做什么。
工程化知识以理解 + 实操为准。