Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第五章:约束即自由 · 代码规范与 Git 工作流

5.1 一个关于“存档”的故事

你一定玩过那种可以“存档”和“读档”的游戏吧?

你在打一个很难的boss之前,小心翼翼地存了个档。然后你冲进去,很快就被boss一巴掌拍死了。没关系,读档,再来一次。你换了一种打法,这次坚持了五分钟,又死了。再读档。这个过程中,你可能会尝试几十种不同的策略,犯下无数个错误,但你知道,屏幕里那个角色的“人生”是绝对安全的。因为你有一个存档。

现在想象一下,如果这个游戏没有存档功能,会怎么样?

你只能一次通关。中途任何一个小失误——不小心掉进陷阱、选错了一条路、打boss时手滑了一下——都意味着你要从第一关开始重新来过。你会玩得心惊胆战,不敢尝试任何新东西,因为你根本输不起。这游戏你大概玩十分钟就想砸键盘了。

你知道吗,我们写代码,本质上就是在玩一个没有存档功能的游戏。

你花了三天时间,把一段代码改来改去,终于让它跑起来了。一切都很完美。第四天,你突然灵光一闪,想出了一个更巧妙的实现方法。你兴奋地开始修改,改着改着,程序崩了。你想回退到昨天的状态,但你发现——你完全记不起来昨天到底改了什么,哪些文件被动了,哪一行是关键。就像那个没有存档的游戏,你一不小心掉进陷阱,就再也回不去了。

这就是为什么,我们需要 Git。

我们这一章要做的事,就是给我们的项目加一套“存档系统”。不仅如此,我们还要给它加一套“自动化检查系统”,保证我们每一次存档都是有意义、有条理、经得起时间考验的。

这套系统的核心组件有四个:

  • Git:那个“存档”和“读档”的工具。
  • ESLint:你的代码拼写和语法检查器。
  • Commitlint:你的存档说明书写规范。
  • Husky:你的自动化守门员。

别看名字花里胡哨,拆开了,一个比一个简单。

5.2 Git:你的时光机,你的存档点

我们首先来搞定 Git。

Git 是什么?一句话,它是一个版本控制工具。但“版本控制”这个词太大了,太抽象了。我们换个说法:Git 就是给代码拍照的工具

你每完成一小段功能,觉得“嗯,现在这个状态是好的,可以记下来”,你就用 Git 给它拍一张“快照”。以后,你随时可以翻阅这些快照,也可以随时回到某一张快照的状态。

这个功能听起来简单,但它彻底改变了我们写代码的方式。

以前我们怎么管理版本?

项目.js
项目_改了一下.js
项目_又改了一下.js
项目_最终版.js
项目_最终版不改了.js
项目_最终版不改了又改了一下.js
...

现在呢?只有一个文件,但它的所有“历史版本”都被 Git 默默记录着。

我们来试试。

首先,确保你的终端在 the118-pTable 项目目录里,然后输入:

git init

看到 Initialized empty Git repository 了吗?这意味着,Git 已经接管了这个目录。它会在里面创建一个叫 .git 的隐藏文件夹,这个文件夹就是它存储所有“快照”的地方。从现在起,这个目录就是一个受 Git 管理的仓库了。

但 Git 有点强迫症。它会默认跟踪这个目录里的每一个文件。可有些文件我们根本不想让它管。比如 node_modules,那个文件夹里有成千上万个我们从来没手动改过的第三方文件,拍它们的快照既浪费空间又浪费时间。

所以我们需要给它写一个“忽略清单”——.gitignore 文件。

在项目根目录创建这个文件,写上:

node_modules
dist

现在,我们来完成第一次存档。存档在 Git 里分两步,这是一个你要牢牢记住的动作组合:

  1. git add .:把当前目录(.代表当前目录)里所有文件,添加到“预备拍照区”。
  2. git commit -m "这里写你做了什么":正式拍下快照,并附上一句说明。
git add .
git commit -m "chore: 项目初始化,搭建基础环境"

成了。你的代码现在有了第一个“存档点”。

以后,你每次想“好了,这个状态值得记住”,就做一遍 add + commit。久而久之,你就有了一本完整的“代码日记”,可以随时翻阅,随时“读档”。

5.3 ESLint:在你写代码的时候就帮你找茬

存档的问题解决了。但我们的代码本身,也可能藏着一些不易察觉的问题。

来看这段代码:

var count = 10;
if (count == "10") {
    console.log("数字是十");
}

这段代码能跑吗?能跑。输出 "数字是十",符合预期。

但它“干净”吗?不,它有几个不干净的地方。

  • var 是 JavaScript 早期的变量声明方式,它的作用域规则很古怪,容易导致意料之外的问题。现代JavaScript都推荐用 let 或 const
  • == 在比较两个值的时候,会尝试进行“类型转换”。这意味着它拿数字 10 和字符串 "10" 比较时,会认为它们是相等的。大多数情况下这并非你想要的,你应该用 === 来做严格比较。
  • 语句结尾缺少分号。JavaScript 有自动补分号的功能,但这个功能有时候反而会“帮倒忙”。

这些问题放在几十行代码里,你一眼就能看出来。但当你的项目膨胀到几百个文件、几千行代码时,你再指望肉眼去检查每一个细节,根本不现实。

这就是 ESLint 要做的事。

ESLint 是一个代码静态分析工具。“静态”的意思是,它不需要运行你的代码,只需要把你的源代码当作文本读一遍,就能找出里面不符合规范的写法。

它就像一个不知疲倦的校对员,在你按下保存键的瞬间,就把那些不规范的、有风险的地方给你标出来。

配置 ESLint

我们的 Vite 模板已经帮我们安装好了 ESLint 的依赖,所以我们可以直接用。

在终端运行:

npx eslint src/

你可能会看到一些警告。别担心,这说明它在干活。它会告诉你:第几行、第几个字符、出了什么问题、违反了哪条规则。

你可以修改 eslint.config.js 来定制规则。比如,如果你在开发中需要 console.log 来调试,可以加上:

rules: {
    'no-console': 'off', // 允许 console
}

有了 ESLint,你的代码就有了最低限度的质量保障。它就像一个保护网,帮你拦下那些因为粗心大意而产生的低级错误。

5.4 Commitlint + Husky:给你的存档加一道质量检查

还记得我们的 git commit -m "存档说明" 吗?那个 -m 后面的引号里,是要写一段话来说明你这次做了什么。

但你有没有见过这样的提交记录?

fix
update
改了改
终于能跑了

一周之后你再回来看,你完全不知道这些“存档”分别对应着什么。这就失去了存档的意义。

我们需要的,是每一次提交都能写清楚:你是做了什么事,是加了新功能,还是修了bug,还是只是改了一下文档?

这就是 Commitlint 出场的时候了。

Commitlint 会强制要求你的提交信息,遵循一套固定的格式。这套格式叫 约定式提交(Conventional Commits),它的基本结构是这样的:

<类型>: <简短描述>

类型必须是下面这些词之一:

类型含义
feat新增了一个功能
fix修了一个 bug
docs只改了文档
style调整了代码格式,不影响运行逻辑
refactor重构了代码,没加新功能也没修 bug
perf做了性能优化
test添加或修改了测试
chore杂务,比如改了配置文件

所以,一个好的提交信息应该是这样的:

git commit -m "feat: 添加元素详情弹出面板"
git commit -m "fix: 修正球体布局中元素计数偏差"

格式统一了,别人(包括三个月后的你自己)一看提交记录,就能清晰地知道这个项目是怎么一步步走到今天的。

但问题来了:我们怎么保证自己每次都会遵守这个格式呢?毕竟人总是会偷懒的,深夜写代码的时候,手一滑就写了一个 "fix" 上去。

这时候,我们需要一个“守门员”——Husky

Husky 能在 Git 执行特定操作之前,自动触发我们预先写好的脚本。它的名字来源于哈士奇,那种精力旺盛、拉着你到处跑的雪橇犬。

我们想要的效果是:每次执行 git commit 的时候,先让 Commitlint 检查一下我写的说明合不合格。合格,放行;不合格,打回去重写。

来,我们把它配好。

  1. 安装依赖
  npm install -D @commitlint/cli @commitlint/config-conventional husky
  1. 初始化 Husky
  npx husky init
这条命令会创建一个 `.husky` 文件夹,专门存放各种“守门员脚本”。
  1. 添加 commit-msg 钩子

    echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
    

就这么简单。现在,你每次 git commit,Commitlint 都会跳出来检查。写得不对?直接拒绝提交。你只能乖乖按照规范重写。

一开始你可能会觉得麻烦,但相信我,习惯成自然。这套规范会保护你的项目历史永远清晰可读。

5.5 让工具再顺手一点

你可能在想:“这个约定式提交的类型好多,我怕记不住。”

没关系。我们的 package.json 里已经配置了一个叫 cz-git 的小工具。它的作用,就是把写提交信息的过程变成一个终端里的交互式菜单

你不需要手动敲 git commit -m "feat: ..." 那么长一串。只需要输入 git cz(或我们配置好的命令),终端会出现一个友好的界面,让你用上下方向键选择“这是什么类型的提交”,然后填写简短描述。一切自动完成,格式永远正确。

好的工具,不会让你觉得被束缚,而是让你感觉“就应该这样用”。

5.6 约束的哲学:围栏不是为了关住你

我们现在回过头来看这一章做的事情。

我们给项目加上了四道约束:

  • Git 管着我们的版本历史
  • ESLint 管着我们的代码风格
  • Commitlint 管着我们的提交信息
  • Husky 在关键时刻执行这些检查

这是不是太多了?我们是不是被绑住了?

我想和你分享一个感受。

在我自己的第一个大型项目中,我没有用任何约束。Git 提交信息随手写,代码风格随心所欲。一开始感觉很自由,像在草原上策马奔腾。但项目做到第三个月的时候,我开始害怕。我不敢改那些“看起来好像不太对”的旧代码,因为我不记得当初为什么那样写;我不敢删掉那些“好像没用”的变量,因为我怕它其实在某个角落被用到了;我甚至不敢看两个月前的提交记录,因为那些记录毫无意义。

那不是自由。那是被自己的混乱所困住。

后来我开始用 Git、用 ESLint、用 Commitlint。一开始确实不适应,感觉处处被管着。但过了一段时间,我意识到,这些约束不是牢笼,而是围栏

它们就像悬崖边上那一道坚固的围栏。当你在晴天白日下慢慢散步时,会觉得它有点碍事。但当你在暴风雨中狂奔时,这道围栏就是唯一能保护你、让你不至于坠入深渊的东西。

所以,约束不是目的,自由才是。 约束只是通往真正自由的那条路。

从下一章开始,我们将正式进入这个项目最令人兴奋的部分——我们不会再用配置文件和各种工具了,我们要开始写真正的核心代码。

我们将从零开始,亲手构建一个数学矩阵引擎。正是它,将赋予我们那118个元素在三维空间中自由驰骋的能力。

准备好了吗?让我们进入那个充满数学之美的新世界。