很多时候,我们看见一些不太优雅的代码、不太整洁的代码,也很容易可以推断出这段代码是怎么来的,甚至是可以推断出写这个代码的人当时的心理状态和那时候的背景。在前端迅速发展的时代,一份一年多两年以上的代码,很可能带着历史的色彩地成为了历史包袱
考古?如果某一天,你突然看见类似下面这些的代码:
多个if-returnfunction f() { if (a) { return } if (b) { return } if (c) { return } // ...many codes } 复制代码推测当事人心理:需求要做这个功能,需要加一个条件。好的,就在最前面加一下
函数有很多参数function f(a, b, c, d, config, isAdmin, isEdit, isAdd) { // ...many codes } 复制代码推测当事人心理:后面这个函数功能更复杂了,需要加多一个参数做配置。啊,还要再加一些功能,再多一个参数
很长的类似的判断if (a === 1 || a === 2 || a === 3 || a === 4) { }推测当事人心理:状态4也要走这个逻辑,那我加多一个&&即可
组件返回值有冗余的短路表达式return ( <> { isXXX && <div>abc</div> } {/* 可能中间有很多其他代码 */} { !isXXX && <div>123</div> } </> ) 复制代码推测当事人心理:兜底情况展示123,直接加一下,ok
明显走不到的逻辑const data = res[0].some_data || {} if (!data) { return } // other code // ================================= const SOME_FLAG1 = 'xxxx1' const SOME_FLAG2 = 'xxxx2' const SOME_FLAG3 = 'xxxx3' const arr = [SOME_FLAG1] if (xxx) { // arr } // 对arr一顿操作 if (!arr) { // 怕出错?加了这个 }推测当事人心理1:可能arr为假值,防止报错
推测当事人心理2:只是改了前面,后面!arr压根没看见
常见的代码片段莫名其妙的出现import { func } from 'prop-types'; function a(){ return 1 } 复制代码推测当事人状况:压根不知道,只是输入了func按了回车,以为是function的补全
想要一些常见的工具函数却代码提示有多个结果发现,package.json里面有lodash,然后自己又写了一个
又比如想要一个request,发现有3个以上的提示。然后一看,团队统一的request一个,某个人写了一个缓存版本又一个,另一个人又自己写了一个袖珍版,最后还有一个人写了一个预处理参数的...
推测当事人状况1:压根不知道有团队统一的request,自己写还写得很挫
推测当事人状况2:知道有request,但不敢改,所以自己封装多一层,但名字还是取了一样的
翻新?开启eslint/tslint本人多次代码优化重构的经验,一个没有lint的项目,开了lint后90%的错误都可以通过autofix解决。例如9000个错误,跑一下即可变成800多个,可以修复那些换行、缩进、函数单参数无括号的问题。这些修复不需要测试介入。剩下的那些错误需要人工解决
最常见的需要人工解决的lint错误合集:
错误
解决方法
紧急程度
风险
下划线命名
全局搜索,一个个人工修
中
低
解构赋值
一般是warning,遇到一个修一个
低
低
无状态class组件
改成PureComponent或者函数组件
高
中
willreciveprops
改成getderivedstatefromprops
高
高
componentwillmount/update
初始state & didmount
高
低
==
确定类型再转化,最后===
高
高
作用域下重复命名
看见就修,但还是有必要性
中
低
ts类型报错
不影响代码的执行,但也不能长期不管
低
低
html标签缺少属性
如img的alt、button的type,看见就修
低
低
promise的rejcet不是error
reject(Error(xxx))
高
低
中等风险以上的修复,需要自测或者测试,过一遍主流程。无论哪一种人工修复,量达到几百个,都需要测试介入
精简if比如上面的多个if-return、很长的类似的判断,都可以精简为||、&&,进一步精简就是数组操作: [1, 2, 3, 4].includes(a),在另一篇文章里面有讲到更多的if简化
完整的看一下整个文件上下文上面提到的一些情况,可能是最开始的时候设计是没什么问题的,但随着需求迭代,就不一样了。比如多个if-return、明显走不到的逻辑、重复写了一些常见的工具函数,这些问题都是因为不完整地看上下文导致的
旧版react的升级迁移旧版升级新版的情况,三个danger的生命周期必须处理掉,其中getderivedstatefromprops风险最大,需要做好各种判断简单的组件尝试使用hook重写(不需要特地去,只是需求涉及到的地方顺便改)用了ref、context的class组件重构为函数组件的时候,记得做好足够的措施,如forwardRef、useContext组件必须带名字,不要export default class extend、export default function() {},否则调试工具不显示名字如果不是ts项目,要使用props-types(或者在旁边写一个d.ts文件)render函数返回内容、函数组件返回内容不宜太长,一定要做好组件拆分。复杂的js运算,建议抽成组件或者renderxxx函数重构步骤本人有多次历史大项目重构经历,常见的case和套路已经在上文提过,接下来是操作步骤的总结
不管三七二十一,先lint【动手前】传统旧项目,通常没有lint,需要自己装。然后找一下团队现在的lint规范(如果没有就找业界出名的如airbnb),接着跑一波lint自动修复,此时可以把缩进换行全部解决,剩下的需要自己手动去修。具体怎么手动修,前面已经提到了
顺便修一下改动文件【动手时】为什么重构?那必然有一个触发点,或是某个需求,或是发现了很多bug导致无法正常运行。或是开始有大力维护的计划。所以先从触发点开始,在保证功能正常的情况下,顺便把模块一起重构了,这里也有几个要领:
新逻辑必须有注释,旧逻辑如果没注释,但又关联这次修改的,自己梳理或者找同事来了解一下背景,补全注释和文档无状态class组件,可以直接改成purecomponent。如果是想拥抱hook,那就必须改函数组件。但是有一个前提:全局搜索一下,确认没有其他地方使用了extend继承当前class组件处理所有的magic number。当你看见if status === 1、 type === 0这种代码,这个让人懵逼的数字就是magic number。此时要看上下文,了解这个数字是什么意思,再使用大写常量来维护(如const NORMAL = 1;,并加上注释:// 【xx模块】状态正常:1) 。如果多个文件用到,需要提取到更上层奇葩命名也是可以在这个环节修复一下。文件内搜索,非对象key、非解构,都可以直接换掉了不确定、不敢改、有疑问的地方,使用TODO注释标记,方便后续回头解决。如if a == b,从代码中无法知道a、b是什么类型,且业务路径很长不好复现,先妥协一下,等有时间再改对于“看不懂”、“不敢改”的函数,你就把它当作一个沙盒就行,能不动的先不要动。时刻记住,保证这段代码上游输入不变即可对于重复的代码,需要提取出来一个函数,然后引进去调用。不必过度封装,大概就一层很薄的封装,能解决掉明显的过多的重复代码即可如果发现not defined的lint报错,但页面没问题,那么只有两种情况:那个文件没用了、那个文件还没执行到。如果是没用的,立刻删掉;如果是没执行到但不敢动的,可以屏蔽掉这行eslint报错并加上TODO,或者直接定义它,让他执行到的时候也能正常且没什么副作用(最好还是要找到相关同事去问一下)修改范围怎么确定?
有一个这样的文件目录:
- components utils.ts constant.ts -- Home index.tsx Header.tsx Footer.tsx ... -- Input index.tsx index.scss ... 如果改的是Header.tsx,Home目录不大,需要全部一起重构;如果Home目录很大,那就只改Header.tsx和他的父组件index.tsx如果Home和Input里面多了任何常量,导致magic number产生,那么必须去constant里面新增定义,并写好注释,再引入到组件里面用如果Home很大,且多个文件import了一个函数,这个函数已经确定没什么用了或者要抽离,此时需要在Home下全局搜索这个语句,直接删掉或者抽离补齐基础设施【动手后】功能已经做好了,也顺便重构了一波,此时还没完。考虑到未来继续维护和重构,所以现在要开始铺路,方便下次,让自己和其他同事更舒服
自己封装的新的关键的函数,需要留下自己的大名、日期,并写好注释,甚至可以把参考的网上的资料链接都贴出来定时查看之前留下的TODO。不要标了TODO就不管了,定时看看,有时间就去跟进解决掉将lint-staged加入到git钩子上,不合格不给提交公共utils、common、assets文件夹都是公共资源,这些需要根据业务共同点抽取出来,包括工具函数、公共组件、全局配置、定义文件readme补全文档,包括页面逻辑、文件目录组织、开发规范,这些都是你这个时候你来定的了js转ts项目,部分迁移,也是改到哪里,哪里就上ts。暂时无法上ts的大组件,先给一个d.ts文件做类型声明也行全透传参数,无法追溯的,只能靠跑业务的时候(如果是服务端的,可以手动curl一下某个接口)console一下获得字段,并抄一份作为注释、文档。node中间层经常会有,params直接透传前端给的转给另一个服务,response也是从另一个服务透传给回前端,或者是{ ...data },经历过的人自然懂这个痛总结虽然平时大家总是自黑,讲段子,发自黑、调侃的表情包,吐槽垃圾代码、历史代码、知道没用但不敢动.jpg。但我相信如果真的搁在头上了,大家都不会怂的,也不会轻易妥协,会认真的修好,完美完成重构
---来自腾讯云社区的---lhyt
微信扫一扫打赏
支付宝扫一扫打赏