4. 放任文件长度,只着眼于当下的需求
很多同学做需求、写代码都比较少从全局考虑,只关注到当前需求如何完成。从“战术”上来说没有问题,快速完成产品的需求、快速迭代产品也是大家希望看到的。
可一旦只关注“战术实现”而忽略“战略设计”,除非做的产品是月抛型的,否则一定会遇到旧逻辑难以修改的情况。
如果再加上一个文件被多达10余人修改过的情况,那么每改一行代码都会是一场灾难,例如最近接手的一个页面:
单文件高达1600多行!哪怕去除300多行的注释,和300多行的模板,剩下的逻辑代码也有1000行左右,这种代码可读性就极其糟糕,必须进行拆分。
而很常见的是,由于每一任经手人都疏于考虑全局,导致大量代码毫无模块化可言,甚至出现多个useEffect的依赖是完全相同的:
这里明显还有另一个问题:滥用hooks。
从行号可以看出来确实是相同的依赖写了多个useEffect,很明显是多个同学各写各的的需求引入的这些hooks。
这代码跑肯定是能跑的,但是很可能会出现多个hooks中修改同一个变量,导致其他地方在使用的时候需要搞一些很tricky的操作来修Bug。5.变量无初始值
在typescript的加持下,对变量的类型定义可以说是日益严格了。可是在一些变量的类型定义比较复杂的情况下,可能一个变量的字段很多、层级很复杂,此时有些同学就可能想偷个懒了,例如:
const [variable, setVariable] = useState<ComplicatedType>(); // some code... const queryData = function() { // some logic setVariable({ show: true }); }; useEffect(() => { queryData(); }, []); return variable.show ? <Component /> : null;
这里的问题很明显,如果queryData耗时比较长,在第一次渲染的时候,最后一行的variable.show
就会报错了,因为variable的初始值是undefined。所以声明变量时,一定要根据变量的类型设置好有效默认值。
6. 三元选择符嵌套使用
网上很多人会推荐说用三元选择符代替简单的if-else,但几乎没有见过有人提到嵌套使用三元选择符的事情,如果看到如下代码,不知道各位读者会作何感想?
{condition1 === 1 ? "数据加载中" : condition2 ? "没有更多了" : condition3 ? "当前没有可用房间" : "数据加载中"}
真的很难理解,明明只是一个简单的提示语句的判断,却需要拿出分析性能的精力去理解,多少有点得不偿失了。
这还只是一种比较简单的三元选择符的嵌套,因为当各个条件分支都为true时,就直接返回了,没有做更多的判断,如果再多做一层,都会直接把人的cpu的干爆炸了。
替代方案:
- 直接用if-else,可读性更高,以后如果要加逻辑也很方便。
- Early Return,也叫卫语句,这种写法能有效简化逻辑,增加可读性。
if (condition1 === 1) return "数据加载中"; if (condition2) return "没有更多了"; if (condition3) return "当前没有可用房间"; return "数据加载中";
虽然不嵌套的三元选择符很简单,但是在例如jsx的模版中,仍然不建议大量使用三元选择符,因为可能会出现如下代码:
return ( condition1 ? ( <div className={condition2 ? cls1 : cls2}> {condition3 ? "111" : "222"} {condition4 ? ( <Component prop1={condition5 ? a : b} /> ) : null </div> ) : ( <Component2> {condition6 ? children1 : children2} </Component2> ) )
类似的代码在我们的项目中频繁出现,模版中大量的三元选择符导致文件内容拉得很长,很容易看着看着就不记得自己在哪个逻辑分支上了。
像这种简单的三元选择符,做成一个简单的memo变量,哪怕是在组件内直接写变量定义(例如:const clsName = condition2 ? cls1 : cls2),最终到模板的可读性也会比上述代码高。
7. 逻辑不拆分
React hooks可以很方便地帮助开发者聚合逻辑抽离成自定义hooks,千万不要把一个页面所有的useState、useEffect等全都放在一个文件中:其实从功能上可以对页面进行拆分,拆分之后这些变量的定义也就可以拆出去了。其中有一个很简单的原则就是,如果一个逻辑同时涉及到了useState和useEffect,那么就可以一并抽离出去成为一个自定义hooks。例如接口请求大家一般都是直接在业务逻辑中做:
const Comp = () => { const [data, setData] = useState({}); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); queryData() .then((response) => { setData(response); }) .catch((error) => { console.error(error); }) .finally(() => { setLoading(false); }); }); if (loading) return "loading..."; return <div>{data.text}</div>; }
根据上面的原则,和数据拉取相关的内容涉及到了useState和useEffect,这整块逻辑就可以拆出去,那么最终就只剩下:
const Comp = () => { const { data, loading } = useQueryData(); if (loading) return "loading..."; return <div>{data.text}</div>; };
这样下来,Comp组件就变得身份清爽了。大家可以参考阿里的ahooks库,里面收集了很多前端常用的hooks,可以极大提升开发效率和减少重复代码。
8. 随意读取window对象的值
作为大型项目,很容易需要依赖别的模板挂载到window对象的内容,读取的时候需要考虑到是否有可能拿不到window对象上的内容,从而导致js报错?例如:
window.tmeXXX.a.func();
如果这个tmeXXX所在的js加载失败了,或者是某个版本中没有a这个属性或者func这个函数,那么页面就会白屏。
好啦,最近CR常出现的8种屎山代码都讲完了,你写过哪几种?你们团队的代码中又有哪些让你一口老血喷出来的不良代码呢?欢迎评论区告诉我。
技术交流群
我建了个前端技术交流群,整体氛围非常好,每天大家都会在群里真诚的交流技术问题,从前端到全栈,从尚未毕业的小年轻,到不惑之年的老大哥,大家都在拼命的汲取知识。最近群里正在组织前端项目实战练习,很快就要开始大干一场了!有想要交流&学习前端知识的老少朋友,欢迎加入我们!