Code Review
CR 可以让每次提交代码都在使仓库变的更好
- 在需求提交前发现 bug
- 代码分享和知识交流
- 利于团队,每个人都对整个项目熟悉,避免个别需求和个别人的强绑定
checklist
- 是否已有现成代码可复用后扩展已有代码
- 是否可以进一步抽象封装
- 是否移除了调试代码
- 是否可以进一步拆分 MR
- commit 描述是否足够清醒
- 是否添加必要注释
- 命名是否合理
- 是否有安全问题
- 是否有坏味道
- 是否有必要的自动化测试
在 CR 之前要提前执行自动化审查
- 代码规范检查
- 代码复杂度检查
- 自动化测试
readability key point
- 代码组织
- 文件命名要有一致性,即合理通用的代码目录组织及命名
- 泛化问题:工具函数、常量应该合理的归类拆分、便于理解。避免命名过于宽泛,形成垃圾桶文件,把所有的公用函数都堆积到一个代码文件内管理
- 代码组织顺序不符合阅读顺序,核心内容上提,私有类和方法下移
- 变量的声明和变量的使用尽量在一起,便于阅读,合理使用换行对函数进行分块
- 类成员按照以下顺序排序
- static > instance
- field > constructor > method
- public > protected > private
- import & export
- 不要
import和require混用,推荐使用 ESM - 同一个文件不要多次
import - 未使用的
import需要被清理 import不要推荐相对路径、推荐使用 alias,设计要有一致性和可读性,如果引用的是当前目录及子目录文件可以使用相对路径import顺序:node 内置模块 -> npm 包 -> 本地文件,每个类型之间加空行import外部依赖命名语义不明确的情况要适当添加注释- 不推荐使用
export default导出,因为引用人可以进行重命名、如果使用类名应该和文件名一致
- 不要
- 命名
- 不要使用关键字命名变量、避免和知名库方法命名重复
- const let var
- 命名格式
- 文件命名使用
kebab-case(烤串命名法),单词全小写,以中横线分隔 - Class 和组件使用
PascalCase(帕斯卡命名法),单词相连,单词首字母大写 - 模块作用域内:
- 变量、常量、函数使用
camelCase(驼峰命名法),单词相连,第一个单词首字母小写,其他单词首字母大写 export的常量使用UPPER_CASE,单词全大写,使用下划线分隔
- 变量、常量、函数使用
- 文件命名使用
- 有意义的命名:文件、函数、类命名要有意义、保持一致性、避免无意义的前后缀
- 布尔类型变量命名使用:
is|should前缀 - 避免无意义后缀,例如函数名包含
Func后缀 - 函数名、参数名和返回结果命名命名要匹配,例如:
const user = getUserInfo()把user改成userInfo和函数名一致会更有可读性
- 布尔类型变量命名使用:
- class 变量要明确声明变量的
public、private、protected
- 语法问题:
- 要使用模板字符串实现字符串拼接
- 添加判断要使用
===,不要使用隐性类型转换 - 函数参数变量保护可以直接使用函数默认参数实现,例:
args = args || {} - 使用 optional chaining(?.)可选链接操作符替换传统的
|| - 使用箭头函数替代
bind实现外层this绑定 - 使用管道(
filter、map)替代for,这样不容易产生很大的循环操作,根据应用场景(过滤、查找、轮询)选择适合的方法 - typescript 函数要声明返回值类型,如果代码可能存在中断(主动退出,或者抛出错误情况)的情况返回类型要包含
never - 优先使用异步函数替换 callback 和 promise
- 使用
new Promise()要注意resolve()和reject()是否正确使用,异常如果直接return就会导致这个 promise 实例一直处于 pending 状态。 - 禁止使用位运算符,可读性差,可以使用
Set实现
- 函数设计
- 单一职责,一个函数只做一件事,例:一个函数根据某个 Boolean 参数来执行两段不同路径,可以将这两段逻辑拆分成两个独立函数。这也是处理过大函数的一种方式,也可以提升代码的可测性。
- 如果函数内有副作用,需要注意函数命名,能够体现包含的副作用
- 参数
- 函数参数要使用 object 类型,方便扩展,便于调用方通过参数名理解参数的含义
- 参数范围要尽量小(精准),不要将全部参数一次性全部传入,例如 react 中直接传入全部 props 或 states
- 异常优先,边界问题优先,优先处理异常逻辑,可以降低代码缩进、缩小代码块长度
- 要对过大函数进行拆分
- 阶段拆分:每个函数内部只处理同一层面的逻辑,如果有更深一级的代码,则将更深一级代码提取成单独函数并在当前函数调用
- 循环拆分:牺牲执行效率,提升代码可读性,将一次循环内要处理的多项任何拆分在多个循环,每个循环只处理一项任务
- 卫语句优化 if 嵌套过深问题,和异常优先原则契合
- 卫语句会导致函数过长,可以将复杂 if 条件提取成函数,可以通过函数名理解判断逻辑
- 代码设计
- 不能使用改变全局对象管理数据、可以使用 redux 等状态管理工具进行管理
- 业务代码里面不应该包含功能代码,应该首先对功能代码进行封装,例:通过网络请求获取信息,不应该在具体的业务代码中处理网络请求功能,应该对网络请求进行、网络异常、业务状态码异常进行封装
- 避免魔术字符串等硬编码行为,应该使用枚举的方式组织、给每一个状态码设置有意义的名字,并且合理的分类规划 namespace
- 避免硬编码,例如:邮件地址、可选项等类型信息要提取配置
- 尽量避免使用
this,需要使用到this的位置可以封装单独函数,直接调用函数将需要的变量传入函数中。 - 类似链接初始化等只需要执行一次的函数,不应该在业务函数内部处理,可以采用单利模式封装,或者使用
once等装饰器实现
- React
- React 使用箭头函数替换 bind 绑定
- 未使用 this 的函数不适应作为成员函数存在,可以提前单独提取
- 使用 react 不用进行 dom 操作
- 其它
- max-length,避免单行过程导致需要横向滚动查看
- 注释:添加合理注释、删除无意义注释代码
- 换行:合理使用换行,段落化代码,避免无意义换行
- 删除调试代码
console等 window.xxxStorage、JSON.parse等方法的调用需要使用try catch语句进行保护- 不能使用
setTimeout进行执行顺序控制
- libs
- 样式处理,styled component > className > 自己拼接 class