前端工程化入门02 - 代码规范

type
status
date
slug
summary
tags
category
icon
password
试想一下,一个几十万行代码的项目,存在几种不同的代码规范,阅读起来是什么感受?连代码缩进使用空格还是 Tab 都能引发不少程序员的争论,可以说统一代码规范是非常重要的事情。
统一代码规范除了刚才所说的两点外,还有其他好处:
  • 规范的代码可以促进团队合作
  • 规范的代码可以降低维护成本
  • 规范的代码有助于 code review(代码审查)
  • 养成代码规范的习惯,有助于程序员自身的成长
当团队的成员都严格按照代码规范来写代码时,可以保证每个人的代码看起来都像是一个人写的,看别人的代码就像是在看自己的代码(代码一致性),阅读起来更加顺畅。更重要的是我们能够认识到规范的重要性,并坚持规范的开发习惯。

如何制订代码规范

代码规范一般包含了代码格式规范、变量和函数命名规范、文档注释规范等等。

代码格式

一般是指代码缩进使用空格还是 Tab、每行结尾要不要加分号、左花括号需不需要换行等等。

命名规范

命名规范一般指命名是使用驼峰式、匈牙利式还是帕斯卡式;用名词、名词组或动宾结构来命名。
变量命名和函数命名的侧重点不同。
变量命名的重点是表明这个变量“是什么”,倾向于用名词命名。而函数命名的重点是表明这个函数“做什么”,倾向于用动宾结构来命名(动宾结构就是 doSomething)。
由于拼音同音字太多,千万不要使用拼音来命名。

文档注释

文档注释比较简单,例如单行注释使用 //,多行注释使用 /**/
如果要让团队从头开始制订一份代码规范,工作量会非常大,也不现实。所以强烈建议找一份比较好的开源代码规范,在此基础上结合团队的需求作个性化修改。
下面列举一些比较出名的 JavaScript 代码规范:
CSS 代码规范也有不少,例如:

注释规范

有同学可能会听过这样一种说法:好的代码是不需要写注释的。其实这种说法有点片面。
如果你写的函数类似于以下这种:
那不写注释很正常,代码逻辑简单,变量、函数命名完全契合代码逻辑。
但在工作中还有很多业务逻辑很复杂的需求,很有可能一个函数要写很多代码,再好的函数命名、变量命名也不一定能看懂代码逻辑。并且有些业务逻辑会跨多个模块,需要跟不同模块的函数打交道。
像这种复杂的代码,还有绕来绕去的业务逻辑,如果不写注释,分分钟变成传说中的“屎山”。
我们平时强调的代码规范、项目规范、重构等等,不就是为了减少沟通,提高开发效率吗。写注释的目的也是为了让代码更加容易理解,以后出问题了,也能快速定位问题,从而解决问题。
所以我觉得这个说法应该这样理解:不是不写注释,而是不写垃圾注释。
什么是垃圾注释?罗里吧嗦一大堆讲不到重要的就是垃圾注释,注释应该着重描述“做了什么”而不是“怎么做”。
例如上面这个函数,你可以这样写注释:“将对象转化为 URL 参数”。也可以这样写:“首先遍历对象,获取每一个键值对,将它们拼在一起,最后在前面补个问号,变成 URL 参数”。
第一个注释虽然描述做了什么,但对于这么简单的函数来说是不用注释的。第二个注释是垃圾注释的典型示例,描述了怎么做。
总的来说,注释是必要的,并且要写好注释,着重描述代码做了什么。如果还有人说不写注释,让他看看 linux 项目去,每一个文件都有注释。

如何检查代码规范

规范制订下来了,那怎么确保它被严格执行呢?目前有两个方法:
  1. 使用工具校验代码格式。
  1. 利用 code review 审查变量命名、注释。
建议使用这两个方法双管齐下,确保代码规范被严格执行。
下面让我们来看一下,如何使用工具来校验代码格式。

ESLint

ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。
  1. 下载依赖
  1. 配置 .eslintrc 文件
  1. package.jsonscripts 加上这行代码 "lint": "eslint --ext .js test/ src/"。然后执行 npm run lint 即可开始验证代码。代码中的 test/ src/ 是要进行校验的代码目录,这里指明了要检查 testsrc 目录下的代码。
不过这样检查代码效率太低,每次都得手动检查。并且报错了还得手动修改代码。
为了改善以上缺点,我们可以使用 VSCode。使用它并加上适当的配置可以在每次保存代码的时候,自动验证代码并进行格式化,省去了动手的麻烦(下一节讲如何使用 VSCode 自动格式化代码)。

Stylelint

Stylelint 是一个开源的、用于检查 CSS 代码格式的开源工具。具体如何使用请看下一节。

VSCode 自动格式化代码

JavaScript 代码

安装 VSCode,然后安装插件 ESLint。
选择 File -> Preference-> Settings(如果装了中文插件包应该是 文件 -> 选项 -> 设置),搜索 eslint,点击 Edit in setting.json
将以下选项添加到配置文件
同时要确保 VSCode 右下角的状态栏 ESlint 是处于工作状态的。如果右下角看不到 Eslint 的标识,请按照上面讲过的步骤打开 setting.json,加上这行代码:
配置完之后,VSCode 会根据你当前项目下的 .eslintrc 文件的规则来验证和格式化代码。

TypeScript代码

下载插件
.eslintrc 配置文件,添加以下两个配置项:
在根目录下的 package.json 文件的 scripts 选项里添加以下配置项:
test/ src/ 是你要校验的目录。修改完后,现在 ts 文件也可以自动格式化了。
如果你使用 Vue-CLI 创建项目,并且想要格式化 TypeScript 的代码,则需要在 .eslintrc.js 文件添加或修改以下几项:
这样就可以格式化 .js .ts .vue 文件了。

CSS 代码

下载依赖
在项目根目录下新建一个 .stylelintrc.js 文件,并输入以下内容:
VSCode 添加 stylelint 插件:
然后就可以看到效果了。
如果你想修改插件的默认规则,可以看官方文档
如果你想格式化 sass scss 文件,则需要下载 stylelint-scss stylelint-config-standard-scss 插件:
注意,要把 stylelint-config-standard 改成 stylelint-config-standard-scss,然后就可以格式化 scss 文件了。

扩展

如何格式化 HTML、Vue(或其他后缀) 文件中的 HTML 代码?
.vue 文件的 HTML 代码可以使用 eslint-plugin-vue 插件来进行格式化:
其他的 HTML 文件需要利用 VSCode 自带的格式化,快捷键是 shift + alt + f。假设当前 VSCode 已经打开了一个 HTML 文件,按下 shift + alt + f 会提示你选择一种格式化规范。如果没提示,那就是已经有默认的格式化规范了,然后 HTML 文件的所有代码都会格式化,并且格式化规则还可以自己配置。

踩坑

Unknown word (CssSyntaxError) 错误

这个问题主要是因为 stylelint 升级到 14 大版本造成的。
解决方案一
安装 stylelint 新的相关依赖:
然后修改 .stylelintrc.js 文件的配置项:
这样修改以后,就不会再报错了。
如果出现 Cannot find module 'postcss-scss' 错误,请将 node_modules package-lock.json 文件删了重新安装。
解决方案二
第二个解决方案就是将以上三个插件的版本降一个大版本就好了,最后的版本如下:
同时需要将 VSCode 的 stylelint 插件降级,现在插件的最新版本是 1.0.3,不支持 stylelint 13 版本。点击插件旁边的小齿轮,再点 Install Another Version,选择其他版本进行安装。
0.87.6 版本安装就可以了,这时 css 自动格式化功能恢复正常。

忽略 .vue 文件中的 HTML 模板验证规则无效

举个例子,如果你将 HTML 模板每行的代码文本长度设为 100,当超过这个长度后 eslint 将会报错。此时如果你还是想超过这个长度,可以选择忽略这个规则:
注意,以上这行忽略验证的代码是不会生效的,因为这个注释是 JavaScript 注释,我们需要将注释改为 HTML 格式,这样忽略验证才会生效:

Code Review 代码审查

代码审查是指让其他人来审查自己代码的一种行为。审查有多种方式:例如结对编程(一个人写,一个人看)或者统一某个时间点大家互相做审查(单人或多人)。
代码审查的目的是为了检查代码是否符合代码规范以及是否有错误,另外也能让评审人了解被审人所写的功能。经常互相审查,能让大家都能更清晰地了解整个项目的功能,这样就不会因为某个核心开发人员离职了而引起项目延期。
当然,代码审查也是有缺点的:一是代码审查非常耗时,二是有可能引发团队成员争吵。据我了解,目前国内很多开发团队都没有代码审查,包括很多大厂。
个人建议在找工作时,可以询问一下对方团队是否有测试规范、测试流程、代码审查等。如果同时拥有以上几点,说明是一个靠谱的团队,可以优先选择。

git 规范

git 规范一般包括两点:分支管理规范和 git commit 规范。

分支管理规范

一个项目可以创建两个分支:master 和 dev。master 对应线上分支,不能直接在 master 分支上写代码,开发时需要从 master 上拉一个 dev 分支进行开发。

开发新功能

当团队成员开发新功能时,需要从 dev 上拉一个 feature-功能名称-开发姓名 分支进行开发,例如:feature-login-tgz。开发完成后需要合并回 dev 分支。

修改 bug

当团队成员修改 bug 时,需要从有 bug 的分支(环境)上拉一个 bug-功能名称-开发姓名 分支进行修复,例如:bug-login-tgz。修复完成后需要合并回原来出现 bug 的分支。
featurebug 开始的分支都属于临时分支,在通过测试并上线后需要将临时分支进行删除。避免 git 上出现太多无用的分支。

合并分支

在将一个分支合并到另一个分支时(例如将 feature-* 合并到 dev),需要查看自己的新分支中有没有多个重复提交或意义不明的 commit。如果有,则需要对它们进行合并(git rebase)。示例:
注意:在将 feature-* 合并到 dev 时,需要先将 dev 分支合并到 feature-* 分支,然后再将 feature-* 合并到 dev 分支,避免出现代码冲突的情况。同理,合并 bug-* 分支也一样。

部署

当 dev 分支通过测试后,就可以合并到 master 进行发布了。

发布时出现的意外情况

举个例子,假设程序要新增 a、b 两个功能,我们的操作流程是这样的:
  1. 从 dev 分支拉两个新分支 feature-a-tgzfeature-b-tgz
  1. 开发完成合并回 dev。
  1. dev 测试完毕后,合并到 master 进行发布。
如果这时突然被告知 b 功能不上,只上 a 功能。我们可以将 feature-a-tgz 分支重新部署到测试环境,这样就不用做任何的代码回滚。只要 feature-a-tgz 分支测试通过就可以直接合到 master 进行线上发布。

git commit 规范

git 在每次提交时,都需要填写 commit message。
commit message 就是对你这次的代码提交进行一个简单的说明,好的提交说明可以让人一眼就明白这次代码提交做了什么。
既然明白了 commit message 的重要性,那我们就更要好好的学习一下 commit message 规范。下面让我们看一下 commit message 的格式:
我们可以发现,commit message 分为三个部分(使用空行分割):
  1. 标题行(subject): 必填, 描述主要修改类型和内容。
  1. 主题内容(body): 描述为什么修改, 做了什么样的修改, 以及开发的思路等等。
  1. 页脚注释(footer): 可以写注释,放 BUG 号的链接。

type部分

commit 的类型:
  • feat: 新功能、新特性
  • fix: 修改 bug
  • perf: 更改代码,以提高性能(在不影响代码内部行为的前提下,对程序性能进行优化)
  • refactor: 代码重构(重构,在不影响代码内部行为、功能下的代码修改)
  • docs: 文档修改
  • style: 代码格式修改, 注意不是 css 修改(例如分号修改)
  • test: 测试用例新增、修改
  • build: 影响项目构建或依赖项修改
  • revert: 恢复上一次提交
  • ci: 持续集成相关文件修改
  • chore: 其他修改(不在上述类型中的修改)
  • release: 发布新版本

scope部分

commit message 影响的功能或文件范围, 比如: route, component, utils, build...

subject部分

commit message 的概述

body部分

具体修改内容, 可以分为多行.

footer部分

一些备注, 通常是 BREAKING CHANGE 或修复的 bug 的链接.

约定式提交规范

  • 每个提交都必须使用类型字段前缀,它由一个名词组成,诸如 featfix ,其后接一个可选的作用域字段,以及一个必要的冒号(英文半角)和空格。
  • 当一个提交为应用或类库实现了新特性时,必须使用 feat 类型。
  • 当一个提交为应用修复了 bug 时,必须使用 fix 类型。
  • 作用域字段可以跟随在类型字段后面。作用域必须是一个描述某部分代码的名词,并用圆括号包围,例如: fix(parser):
  • 描述字段必须紧接在类型/作用域前缀的空格之后。描述指的是对代码变更的简短总结,例如: fix: array parsing issue when multiple spaces were contained in string.
  • 在简短描述之后,可以编写更长的提交正文,为代码变更提供额外的上下文信息。正文必须起始于描述字段结束的一个空行后。
  • 在正文结束的一个空行之后,可以编写一行或多行脚注。脚注必须包含关于提交的元信息,例如:关联的合并请求、Reviewer、破坏性变更,每条元信息一行。
  • 破坏性变更必须标示在正文区域最开始处,或脚注区域中某一行的开始。一个破坏性变更必须包含大写的文本 BREAKING CHANGE,后面紧跟冒号和空格。
  • BREAKING CHANGE: 之后必须提供描述,以描述对 API 的变更。例如: BREAKING CHANGE: environment variables now take precedence over config files.
  • 在提交说明中,可以使用 featfix 之外的类型。
  • 工具的实现必须不区分大小写地解析构成约定式提交的信息单元,只有 BREAKING CHANGE 必须是大写的。
  • 可以在类型/作用域前缀之后,: 之前,附加 ! 字符,以进一步提醒注意破坏性变更。当有 ! 前缀时,正文或脚注内必须包含 BREAKING CHANGE: description

示例

fix(修复BUG)

每次 git commit 最好加上范围描述。
例如这次 BUG 修复影响到全局,可以加个 global。如果影响的是某个目录或某个功能,可以加上该目录的路径,或者对应的功能名称。

feat(添加新功能或新页面)

chore(其他修改)

chore 的中文翻译为日常事务、例行工作。顾名思义,即不在其他 commit 类型中的修改,都可以用 chore 表示。
其他类型的 commit 和上面三个示例差不多,在此不再赘述。

验证 git commit 规范

利用 git hook 能在特定的重要动作发生时触发自定义脚本。
验证 git commit 规范也不例外,我们需要通过 git 的 pre-commit 钩子函数来进行。当然,你还需要下载一个辅助插件 husky 来帮助你进行验证。
pre-commit 钩子在键入提交信息前运行,它用于检查即将提交的快照。
husky 是一个开源的工具,使用它我们可以配置相关的 git hook 脚本。下面让我们看一下如何使用:
下载
第一次使用需要执行 husky install 初始化,但是为了方便,最好把命令写到 package.json 里,以后每次重新安装依赖都会自动初始化:
初始化后,根目录下会生成一个 .husky 目录(这个一般情况下不需要动)。接下来就可以自己添加需要监听的 git 相关钩子了。
这行命令会成一个 pre-commit 文件(文件内容为 npm run lint),在 git 的 pre-commit 钩子触发时,就会去执行这个文件的代码。
这里介绍一下比较常用的三个钩子的含义:
  1. pre-commit,在 git commit 提交消息前执行,一般用来执行 lint,校验代码格式。
  1. commit-msg,在 git commit 提交消息时执行,一般用来验证消息是否符合规范。
  1. pre-push,在推送前执行,一般用于执行测试。
现在我们来实践一下,创建一个 commit-msg 钩子文件。
首先在项目根目录下新建一个文件夹 scripts,并在下面新建一个文件 verify-commit.js,输入以下代码:
然后再创建 commit-msg 钩子文件
这样每次提交消息的时候,都会执行 FORCE_COLOR=1 node scripts/verify-commit.js $1 去验证消息格式了(FORCE_COLOR=1 是为了在终端让文字可以显示颜色,$1 是 git commit msg 的文件地址)。
通过工具,我们可以很好的管理团队成员的 git commit 格式,无需使用人力来检查,大大提高了开发效率。
另外,我提供了一个简单的工程化 DEMO。它包含了自动格式化代码和 git 验证,如果看完文章还是不知道如何配置,可以参考一下。

lint-staged

使用 lint-staged 可以只对 git 暂存区上的文件进行校验,不需要对所有的文件进行 lint 检查。
安装
将原来 package.json 文件中的代码:
改为:
文件过滤说明:
多个后缀匹配:

项目规范

项目规范主要是指项目文件的组织方式和命名方式。统一项目规范是为了方便管理与修改,不会出现同样性质的文件出现在不同的地方。例如同样是图片,一个出现在 assets 目录,一个出现在 img 目录。
创建目录,需要按照用途来划分。例如较常见的目录有:文档 doc、资源 src、测试 test...
src 资源目录又可以细分:
现在文件命名有很多种方式(是否简写 img image、是否复数 img imgs、文件名过长是用驼峰还是用-连接 oneTwo one-two)。其实用哪种方式不重要,最重要的是命名方式一定要统一。
例如团队成员有人命名目录喜欢用复数形式(apis),有人喜欢用单数(api),这样是不允许的,一定要统一。

UI 规范

注意,这里的 UI 规范是指项目里常用 UI 组件的表现方式以及组件的命名方式,而不是指 UI 组件如何设计。

表现方式

现在开源的 UI 组件库有很多,不同的组件库的组件表现方式也不一样。例如有些按钮组件点击时颜色变深,有些组件则是变浅。所以建议在 PC 端和移动端都使用统一的 UI 组件库(PC 端、移动端各一个),或者同一个项目里只使用一个 UI 组件库。
另外,项目里常用的组件表现方式也需要通过文档确定下来。例如收缩展开的动画效果,具体到动画持续时间、动画是缓进快出还是快进缓出等等。
如果不把这些表现方式的规范确定下来,就有可能出现以下这种情况:
  1. 同样的组件,在不同的页面有不同的表现方式(例如动画效果)。因为没有规范,开发根据个人喜好添加表现效果。
  1. 同样的二次确认弹窗,提示语不一样,按钮类型也不一样。

统一命名

统一命名,也是为了减少沟通成本。
举个例子,现在的日期组件可以选单个日期、也可以选择范围日期,有的还可以选择时间。这样一来,一个日期组件就有四种情况:
  1. 单个日期带时间
  1. 单个日期不带时间
  1. 日期范围带时间
  1. 日期范围不带时间
如果这种情况不区分好,开发在看产品文档的时候就会疑惑,从而增加了开发与产品的沟通成本。
综上所述,我们可以发现制定 UI 规范的好处有两点:
  1. 统一页面 UI 标准,节省 UI 设计时间。
  1. 减少沟通成本,提高前端开发效率。

小结

其实统一规范的最根本目的就是为了保证团队成员的一致性,从而减少沟通成本,提高开发效率。我以前就经历过因为规范不标准,造成产品与开发理解有偏差、开发各写各的代码,导致各种 BUG 不断,最后项目延期的事。
所以说为了提高开发效率,减少加班,请一定要统一规范。

参考资料

前端工程化入门03 - 前端组件化前端工程化入门01 - 技术选型
前端技术
AI技术
拙见
工具应用
其他知识
编程知识