包阅导读总结
1. 关键词:Code Review、前端、代码优化、AI 工具、逻辑优化
2. 总结:本文介绍了前端代码审查(Code Review),包括其定义、作用、做法,如从项目架构规范等方面入手,提到借助 AI 工具可提高效率但仍需人工总结,还列举了多种代码优化方式,强调优化核心是让代码更简洁、易理解和维护。
3. 主要内容:
– 前端代码审查
– 定义及范围:Code Review 即代码审查,通常关注代码规范和逻辑,组长可酌情考虑业务。
– 必要性:视公司业务情况而定,能避免生产事故,锻炼自身能力。
– 如何进行代码审查
– 入手方面:项目架构规范、代码编写规范、代码逻辑与优化、业务需求。
– 具体做法:传统的 PR 时查看与备注,定期开 CR 分享会。
– 借助 AI 工具:如 CodeGeex、CodeRabbit 等,可减少阅读代码环节,但仍需人工改进和总结。
– 代码优化实践
– 判断逻辑优化:包括深层对象判空、空函数判断、复杂判断逻辑抽离、梳理判断处理逻辑。
– 函数传参优化:将多个形参优化为一个对象参数。
– 命名注释优化:避免魔法数字、写有意义的注释、合理利用命名空间。
– 分支逻辑优化:用映射代替分支逻辑。
– 对象赋值优化:使用展开运算符保留原属性。
– 隐式耦合优化:使用常量减少耦合影响。
思维导图:
文章地址:https://juejin.cn/post/7394792228215128098
文章来源:juejin.cn
作者:大麦大麦
发布时间:2024/7/24 2:01
语言:中文
总字数:2992字
预计阅读时间:12分钟
评分:86分
标签:代码审查,前端开发,AI 工具,代码优化,团队协作
以下为原文内容
本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com
前言
Code Review
是什么?
Code Review
通常也简称 CR
,中文意思就是 代码审查
一般来说 CR
只关心代码规范和代码逻辑,不关心业务
但是,如果CR
的人是组长,建议有时间还是看下与自己组内相关业务,能避免一些生产事故的发生
作为前端组长做 Code Review
有必要吗?
主要还是看公司业务情况吧,如果前端组长需求不多的情况,是可以做下CR
,能避免一些生产事故
- 锻炼自己的
CR
能力 - 看看别人的代码哪方面写的更好,学习总结
- 和同事交流,加深联系
- 你做了
CR
,晋升和面试,不就有东西吹了不是
那要怎么去做Code Review
呢?
可以从几个方面入手
- 项目架构规范
- 代码编写规范
- 代码逻辑、代码优化
- 业务需求
具体要怎么做呢?
传统的做法是PR
时查看,对于不合理的地方,打回并在PR
中备注原因或优化方案
每隔一段时间,和组员开一个简短的CR
分享会,把一些平时CR
过程中遇到的问题做下总结
当然,不要直接指出是谁写出的代码有问题,毕竟这不是目的,分享会的目的是交流学习
人工CR
需要很大的时间精力,与心智负担
随着 AI 的发展,我们可以借助一些 AI 来帮我们完成CR
接下来,我们来看下,vscode
中是怎么借助 AI 工具来 CR
的
安装插件 CodeGeex
新建一个项目
mkdir code-reviewcd code-review
创建 test.js
并用 vscode 打开
cd .>test.jscode ./
编写下 test.js
function checkStatus() { if (isLogin()) { if (isVip()) { if (isDoubleCheck()) { done(); } else { throw new Error("不要重复点击"); } } else { throw new Error("不是会员"); } } else { throw new Error("未登录"); }}
这是连续嵌套的判断逻辑,要怎么优化呢?
侧边栏选择这个 AI 插件,选择我们需要CR
的代码
输入 codeRiview
,回车
我们来看下 AI 给出的建议
AI 给出的建议还是很不错的,我们可以通过更多的提示词,优化它给出的修改建议,这里就不过多赘述了
通常我们优化这种类型的代码,基本优化思路也是,前置校验逻辑,正常逻辑后置
除了CodeGeex
外,还有一些比较专业的 codeRiview
的 AI 工具
比如:CodeRabbit
那既然都有 AI 工具了,我们还需要自己去CR
吗?
还是有必要的,借助 AI 工具我们可以减少一些阅读大量代码环节,提高效率,减少 CR
的时间
但是仍然需要我们根据 AI 工具的建议进行改进,并且总结,有利于拓宽我们见识,从而写出更优质的代码
具体 CR 实践
判断逻辑优化
1. 深层对象判空
if ( store.getters && store.getters.userInfo && store.getters.userInfo.menus) {}
if (store?.getters?.userInfo?.menus) {}
2. 空函数判断
优化之前
props.onChange && props.onChange(e)
支持 ES11 可选链写法,可这样优化,js 中需要这样,ts 因为有属性校验,可以不需要判断,当然也特殊情况
props?.onChange?.(e)
老项目,不支持 ES11 可以这样写
const NOOP = () => 8const { onChange = NOOP } = propsonChange(e)
3. 复杂判断逻辑抽离成单独函数
function checkGameStatus() { if (remaining === 0 || (remaining === 1 && remainingPlayers === 1) || remainingPlayers === 0) { quitGame() }}
function isGameOver() { return ( remaining === 0 || (remaining === 1 && remainingPlayers === 1) || remainingPlayers === 0 );}function checkGameStatus() { if (isGameOver()) { quitGame(); }}
4. 判断处理逻辑正确的梳理方式
function checkStatus() { if (isLogin()) { if (isVip()) { if (isDoubleCheck()) { done(); } else { throw new Error('不要重复点击'); } } else { throw new Error('不是会员'); } } else { throw new Error('未登录'); }}
这个是不是很熟悉呀~
没错,这就是使用 AI 工具 CR
的代码片段
通常这种,为了处理特殊状况,所实现的判断逻辑,都可以采用 “异常逻辑前置,正常逻辑后置” 的方式进行梳理优化
function checkStatus() { if (!isLogin()) { throw new Error('未登录'); } if (!isVip()) { throw new Error('不是会员'); } if (!isDoubleCheck()) { throw new Error('不要重复点击'); } done();}
函数传参优化
const getMyInfo = ( name, age, gender, address, phone, email,) => { }
有时,形参有非常多个,这会造成什么问题呢?
- 传实参是的时候,不仅需要知道传入参数的个数,还得知道传入顺序
- 有些参数非必传,还要注意添加默认值,且编写的时候只能从形参的后面添加,很不方便
- 所以啊,那么多的形参,会有很大的心智负担
怎么优化呢?
const getMyInfo = (options) => { const { name, age, gender, address, phone, email } = options; }getMyInfo( { name: '张三', age: 18, gender: '男', address: '北京', phone: '123456789', email: '123456789@qq.com' })
你看这样是不是就清爽了很多了
命名注释优化
1. 避免魔法数字
if (state === 1 || state === 2) { } else if (state === 3) { }
咋一看,这 1、2、3 又是什么意思啊?这是判断啥的?
语义就很不明确,当然,你也可以在旁边写注释
更优雅的做法是,将魔法数字改用常量
这样,其他人一看到常量名大概就知道,判断的是啥了
const UNPUBLISHED = 1;const PUBLISHED = 2;const DELETED = 3;if (state === UNPUBLISHED || state === PUBLISHED) { } else if (state === DELETED) { }
2. 注释别写只表面意思
注释的作用:提供代码没有提供的额外信息
let id = 1 let id = 1
3. 合理利用命名空间缩短属性前缀
class User { userName; userAge; userPwd; userLogin() { }; userRegister() { };}
如果我们把前面的类里面,变量名、函数名前面的 user
去掉
似乎,也一样能理解变量和函数名称所代表的意思
代码却,清爽了不少
class User { name; age; pwd; login() {}; register() {};}
分支逻辑优化
什么是分支逻辑呢?
使用 if else、switch case ...
,这些都是分支逻辑
const statusMap = (status: string) => { switch(status) { case 'success': return 'SuccessFully' case 'fail': return 'failed' case 'danger' return 'dangerous' case 'info' return 'information' case 'text' return 'texts' default: return status }}
const statusMap = (status: string) => { if(status === 'success') return 'SuccessFully' else if (status === 'fail') return 'failed' else if (status === 'danger') return 'dangerous' else if (status === 'info') return 'information' else if (status === 'text') return 'texts' else return status}
这些处理逻辑,我们可以采用 映射代替分支逻辑
const STATUS_MAP = { 'success': 'Successfull', 'fail': 'failed', 'warn': 'warning', 'danger': 'dangerous', 'info': 'information', 'text': 'texts'}return STATUS_MAP[status] ?? status
【扩展】
??
是 TypeScript
中的 “空值合并操作符”
当前面的值为 null
或者 undefined
时,取后面的值
对象赋值优化
const setStyle = () => { content.body.head_style.style.color = 'red' content.body.head_style.style.background = 'yellow' content.body.head_style.style.width = '100px' content.body.head_style.style.height = '300px' }
这样一个个赋值太麻烦了,全部放一起赋值不就行了
可能,有些同学就这样写
const setStyle = () => { content.body.head_style.style = { color: 'red', background: 'yellow', width: '100px', height: '300px' }}
咋一看,好像没问题了呀?那 style
要是有其他属性呢,其他属性不就直接没了吗~
const setStyle = () => { content.body.head_style.style = { ...content.body.head_style.style color: 'red', background: 'yellow', width: '100px', height: '300px' }}
采用展开运算符,将原属性插入,然后从后面覆盖新属性,这样原属性就不会丢了
隐式耦合优化
function responseInterceptor(response) { const token = response.headers.get("authorization"); if (token) { localStorage.setItem('token', token); }}function requestInterceptor(response) { const token = localStorage.getItem('token'); if (token) { response.headers.set("authorization", token); }}
这个上面两个函数有耦合的地方,但是不太明显
比如这样的情况,有一天,我不想在 responseInterceptor
函数中保存 token
到 localStorage
了
function responseInterceptor(response) { const token = response.headers.get("authorization");}function requestInterceptor(response) { const token = localStorage.getItem('token'); if (token) { response.headers.set("authorization", token); }}
会发生什么?
localStorage.getItem('token')
一直拿不到数据,requestInterceptor
这个函数就报废了,没用了
函数 responseInterceptor
改动,影响到函数 requestInterceptor
了,隐式耦合了
怎么优化呢?
const TOKEN_KEY = "authorization";const TOKEN = 'token';function responseInterceptor(response) { const token = response.headers.get(TOKEN_KEY); if (token) { localStorage.setItem(TOKEN_KEY, token); }}function requestInterceptor(response) { const token = localStorage.getItem(TOKEN_KEY); if (token) { response.headers.set(TOKEN_KEY, token); }}
这样做有什么好处呢?比刚才好在哪里?
还是刚才的例子,我去掉了保存 localStorage.setItem(TOKEN_KEY, token)
我可以根据TOKEN_KEY
这个常量来查找还有哪些地方用到了这个 TOKEN_KEY
,从而进行修改,就不会出现冗余,或错误
不对啊,那我不用常量,用token
也可以查找啊,但你想想 token
这个词是不是得全局查找,其他地方也会出现token
查找起来比较费时间,有时可能还会改错了
用常量的话,全局查找出现重复的概率很小
而且如果你是用 ts 的话,window 下鼠标停在常量上,按 ALT
键就能看到使用到这个常量的地方了,非常方便
小结
codeRiview(代码审查)不仅对个人技能的成长有帮助,也对我们在升职加薪、面试有所裨益
CR
除了传统的方式外,也可以借助 AI 工具,来简化其中流程,提高效率
上述的优化案例,虽然优化方式不同,但是核心思想都是一样,都是为了代码 更简洁、更容易理解、更容易维护
当然了,优化方式还有很多,如果后期遇到了也会继续补充进来