🐯 前言
为啥写这篇文章?现在网上说的代码优化,大部分都是性能优化。前端业务代码大部分情况不需要考虑性能,个人觉得网上代码优化的文章并不实用。因此开此篇来论述我心中那像诗一样的代码。
🐯 啥是好代码?
网上的标准也很多,最核心最实用的那一条是:让人看得懂的代码!这是我个人多年实战+思考的答案。代码本身就是人与机器交互的工具,如果你的代码晦涩难,那肯定就是菜鸡代码,代码是给人看的而不是给机器看的,机器看的是二进制的10而不是代码。
下述的代码优化内容都是以《让人看得懂的代码》为核心。为啥我长篇大论《让人看得懂的》这个代码思路?因为现实中很多人写代码,从来没有思考过这个事情,写了好几年代码,一样菜得抠脚。写好代码核心是方向对,只要思想不滑坡,啥事都好说。愿各位的代码都能像诗一样优雅。
优化一 : forEach乱用,API乱用
很久以前面试的时候,面试官提的一个问题:在用forEach的时候,如何跳出整个循环?我没答上,只记得需要用特殊的方式才能跳出forEach,后面回家查才知道是哪个方式。
当我责怪自己这么基础的都不知道,突然察觉不对,如果要跳出循环,为啥不用find或者some,为啥用forEach?forEach根本就不是干这个事的,我用forEach从来就没跳出过循环。是面试官傻逼,不是我傻逼。面试官他用forEach,但根本就没思考为啥用forEach。
写一篇文章,'的','得','地'即使乱用,大部分人也都能理解文章的意思。就像我们代码的forEach,find,some,every乱用。但一篇流畅的文章,'的地得'的使用必定是正确的。别人看你代码,看到forEach就是默认它是遍历完整个循环,结果你非要整活跳出forEach,别人不会觉得你牛逼,居然还能跳出forEach循环,只会觉得你是个憨,为啥要用这么变扭的方式,白白损害代码的阅读性。
api是一个英文单词,不是a1,a2,a3。每个api都是有自己的语义的,有自己的使命的。比如every,当遍历空数组时,返回的是true而是false。
javascript
let arr = []
const bolEvery = arr.every(item => item === 3)
const bolSome = arr.some(item => item === 3)
// bolEvery为true,而不是false。bolSome却为true。
因为every检测整个数组每一项是否都符合标准,空数组没有内容,所以属于都符合则返回true,这正好对应every的中文翻译:每个。而some的中文翻译是:某个,则检测整个数组某项是否都符合标准,只有有一项符合标准,就是true。而空数组,自然是false。
这个forEach的问题是个小问题,其实只是个引子,核心是代码思路。在用api前,先思考这个api到底是啥意思。每个api都用准确的代码,就像一篇用词精准的文章,能大大减轻代码的阅读难度。
写好一份vue或react代码,很多人把精力投入到了源码中,但把它的官方api文档重新看一次,收获或许更多。
优化二: 减少不必要的流程,降低代码复杂性
功能需求
一共三种状态,默认图片背景,自定义图片背景,自定义颜色背景。单选,只能有一种状态存在。
原先的代码
javascript
// 操作类型 1 颜色 2 图片 3 默认图片
let dealType: Ref<number> = ref(1)
const listImg: any = ref([])
const clickReviewImage = (index, elIndex) => {
dealType.value = 3
name.value = listImg.value[index]?.[elIndex]?.diyImageInfoName
norm.value.backgound = [
{
url: listImg.value[index]?.[elIndex]?.diyImageInfoUrl,
height: 240
}
]
}
const isShow = (index, elIndex) => {
return status.value === 3 && listImg.value[index]?.[elIndex]?.diyImageInfoName === name.value
}
const deleteImage = () => {
style.value.background = color.value
status.value = dealType.value = 1
}
... 下面还有一堆代码
还有一堆代码省略了,都是围绕dealType状态更改的代码,大家就不用看了,因为我也没看,我直接重构了。需求明明不复杂,但能整得这么复杂,我也是醉了。
优化后的代码
javascript
//1 颜色
const color = ref('')
//2 图片
const bg = ref('')
//3 默认图片
const defaultBg = ref('')
// 设置颜色
funtion setColor(value){
color.value = value
bg.value = ''
defaultBg.value = ''
}
优化后就变得很简单很清晰了。之前他包多了一层,用状态去控制页面的展示内容,我直接把它状态那层中间商直接删了,逻辑立刻简化了。
这里并不是说,在页面增加一层状态管理是错的,在复杂情况时,增加一层状态管理可以使逻辑更加清晰,能大大提升代码阅读性。错的是乱加状态管理!
什么时候加状态管理比较好?这问题很简单,视图内容复杂的时候。
举个例子:本来实现这个功能时,我是没有加状态的,和上面的优化后的例子一样,使用几个参数(比如isShow:是否显示占位图片)来控制视图,但发现需要控制的内容太多了,且几种参数也会有交集。因此马上改用状态管理,把页面的几种状态管理起来,再用状态去影响视图参数。大大增加的代码的可阅读性。
备注:使用状态管理时,注释一定要清晰,否则阅读性一样低,纯属挑水淋石头,无用功。
javascript
/** 当前template状态
状态: 1.是容器但不能移动。
当父级不是容器,自身是容器,就会这种状态。
2.不是容器能移动
普通组件状态。
3.是容器也能移动
父级是容器,本身也是容器。
4.不能移动也不能当容器。(根节点下的内容,不能移动也不能当容器,暂时搁置先)
备注:默认是普通组件(不是容器能移动),当父级不是容器时,子级都是不可移动的。
*/
let type: '1' | '2' | '3' = '2'
setType()
function setType() {
const parentIsContainer = inject<any>('compParent');
//若组件父级是容器,自身也是容器,则状态为3
if (isContainer && parentIsContainer?.isContainer) {
type = '3'
}
//若组件父级不是容器,自身也是容器,则状态为1
else if (isContainer) {
type = '1'
}
}
其实这个代码优化思路和第一个思路差别不大,万变不离其宗。就是写代码前,先思考,我写这一段代码的目的是啥?是否有用?带着思考去写代码,代码就会越来越优雅。
题外话
如果那个辛辛苦苦写了一堆代码,结果被我重构的前端,看到我的这篇文章,可能会觉得我纯属脱裤子放屁,多余。这里说说我的看法,没有人喜欢重构别人的代码,但遇到逻辑复杂的功能,菜鸡代码和优秀代码是天壤之别。
我重构他代码的原因,并非他代码写得水,而是因为这里的业务是要在两端共用的,他在这一端搞了个无用的状态管理,那在另外一端,也得加这一套东西,而两端的业务需求又是不太一样的,因此还得整两套逻辑去维护他的这个无用的状态管理。这TMD谁顶得住啊。他外包搞完这个项目就走了,后面全得我维护,我不重构,后面一堆摊子等着我。
在这里也求求一些写代码很随意的小伙伴,我也不想卷,但垃圾代码遇到复杂功能,绝对是噩梦中的噩梦,大家都是打工人,打工人何苦为难打工人。
另外说一句,上个前端小伙伴,你的代码是有点水,我重构你的一个功能,我能提出好几个代码点。。。下面的优化3和优化4,也都是重构他代码得到的。
优化三: 自动化导入,逻辑更加清晰
一些重复的批量导入,有api批量导入。这样逻辑清晰,活也少。
优化前
javascript
import default01 from '../../../imgs/inside/01.png'
import default02 from '../../../imgs/inside/02.png'
import default03 from '../../../imgs/inside/03.png'
import default04 from '../../../imgs/inside/04.png'
import default05 from '../../../imgs/inside/05.gif'
import default06 from '../../../imgs/inside/06.gif'
// 默认图片
const defaultImgList = [
{
name: '01.png',
url: default01
},
{
name: '02.png',
url: default02
},
{
name: '03.png',
url: default03
},
....省略代码
优化后
javascript
getLocalityImageList();
function getLocalityImageList() {
const modulesFiles = import.meta.glob("../../../imgs/inside/*", {
eager: true,
}) as any;
for (const path in modulesFiles) {
const arr = path.split("/");
defaultImgList.push({
url: modulesFiles[path].default,
name: arr[arr.length - 1],
});
}
}
优化思路
没啥思路,遇到批量导入的需求,就该这样做。批量导入,后期也好维护很多。就像上面的例子,如果文件夹里要新增一张图片,不用批量导入,那我还得手动去引入一张新的图片,这多傻啊。
优化四: 能集中管理的数据与方法,就尽量集中
我没搞懂上一个小伙伴为啥非要每个页面都重复同一个方法。且他写得还不好,我还得一个个页面去删他的代码,折磨。一样的方法,肯定放在一个地方管理,然后各个地方引用,即省事,维护性也高。
结束语
好的代码其实并不难,抓住一个核心点:《代码是写给人看的》。 只要思想不滑坡,代码就会越写越好。
代码优化的点,还有很多,有喜欢的小伙伴,帮忙点个赞,我下期再更。本来以为内容会很少,没想到写了快半天,2000多个字,看来我对垃圾代码的怨气还是蛮深的,哈哈哈哈,这不能怪我,谁也不喜欢重构别人的代码,重构代码,贼容易出事。
再次祝愿大家的代码像山一样强壮,像诗一样优雅。