前端代码考(算)古(帐)与翻(重)新(构)

不少时候,咱们看见一些不太优雅的代码、不太整洁的代码,也很容易能够推断出这段代码是怎么来的,甚至是能够推断出写这个代码的人当时的心理状态和那时候的背景。在前端迅速发展的时代,一份一年多两年以上的代码,极可能带着历史的色彩地成为了历史包袱css

考古🔍

若是某一天,你忽然看见相似下面这些的代码:html

多个if-return

function 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多个,能够修复那些换行、缩进、函数单参数无括号的问题。这些修复不须要测试介入。剩下的那些错误须要人工解决node

最多见的须要人工解决的lint错误合集:
错误 解决方法 紧急程度 风险
下划线命名 全局搜索,一个我的工修
解构赋值 通常是warning,遇到一个修一个
无状态class组件 改为PureComponent或者函数组件
willreciveprops 改为getderivedstatefromprops
componentwillmount/update 初始state & didmount
== 肯定类型再转化,最后===
做用域下重复命名 看见就修,但仍是有必要性
ts类型报错 不影响代码的执行,但也不能长期无论
html标签缺乏属性 如img的alt、button的type,看见就修
promise的rejcet不是error reject(Error(xxx))

中等风险以上的修复,须要自测或者测试,过一遍主流程。不管哪种人工修复,量达到几百个,都须要测试介入react

精简if

好比上面的多个if-return、很长的相似的判断,均可以精简为||&&,进一步精简就是数组操做: [1, 2, 3, 4].includes(a),在另外一篇文章里面有讲到更多的if简化git

完整的看一下整个文件上下文

上面提到的一些状况,多是最开始的时候设计是没什么问题的,但随着需求迭代,就不同了。好比多个if-return、明显走不到的逻辑、重复写了一些常见的工具函数,这些问题都是由于不完整地看上下文致使的json

旧版react的升级迁移

  • 旧版升级新版的状况,三个danger的生命周期必须处理掉,其中getderivedstatefromprops风险最大,须要作好各类判断
  • 简单的组件尝试使用hook重写(不须要特意去,只是需求涉及到的地方顺便改)
  • 用了ref、context的class组件重构为函数组件的时候,记得作好足够的措施,如forwardRef、useContext
  • 组件必须带名字,不要export default class extendexport default function() {},不然调试工具不显示名字
  • 若是不是ts项目,要使用props-types(或者在旁边写一个d.ts文件)
  • render函数返回内容、函数组件返回内容不宜太长,必定要作好组件拆分。复杂的js运算,建议抽成组件或者renderxxx函数

重构步骤

本人有屡次历史大项目重构经历,常见的case和套路已经在上文提过,接下来是操做步骤的总结数组

无论三七二十一,先lint【动手前】

传统旧项目,一般没有lint,须要本身装。而后找一下团队如今的lint规范(若是没有就找业界出名的如airbnb),接着跑一波lint自动修复,此时能够把缩进换行所有解决,剩下的须要本身手动去修。具体怎么手动修,前面已经提到了promise

顺便修一下改动文件【动手时】

为何重构?那必然有一个触发点,或是某个需求,或是发现了不少bug致使没法正常运行。或是开始有大力维护的计划。因此先从触发点开始,在保证功能正常的状况下,顺便把模块一块儿重构了,这里也有几个要领:缓存

  • 新逻辑必须有注释,旧逻辑若是没注释,但又关联此次修改的,本身梳理或者找同事来了解一下背景,补全注释和文档
  • 无状态class组件,能够直接改为purecomponent。若是是想拥抱hook,那就必须改函数组件。可是有一个前提:全局搜索一下,确认没有其余地方使用了extend继承当前class组件
  • 处理全部的magic number。当你看见if status === 1type === 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。但我相信若是真的搁在头上了,你们都不会怂的,也不会轻易妥协,会认真的修好,完美完成重构

关注公众号《不同的前端》,以不同的视角学习前端,快速成长,一块儿把玩最新的技术、探索各类黑科技

相关文章
相关标签/搜索