场景
在一个风和日丽的清晨,刚进公司我就看到测试小哥眉头紧锁,疯狂的在工位上面摆弄自己的鼠标。突然抬头看到我进来,急急忙忙的说:火锅哥,快过来搂一眼!正式环境上突然图表的数据没了。昨天还好好的,今天早上领导突然要看效果,把链接一打开进入数据大屏就不行了。
看着测试要背锅的样子,只能帮忙排查一下问题呗! 如果是没有数据,那咱就先抓包看一下~
但是看到网络请求里面,后端所有数据都是正常返回的,为什么没渲染呢? 难道是前端代码报错导致打断了渲染?
咱从网络又切换到了控制台~ 好家伙果然是报错了。

然后马上打开了vscode,找到了这行代码的逻辑,如下:

原因就是res是后端接口的返回,按正常情况应该返回一个Array数组,结果实际返回了一个"null",那自然而然就JS报错了呗!
于是乎找到后端大佬说明了情况,谁知大佬这天来了大姨妈,心情及其暴躁~ 吼着说: 逻辑不会永远按正常情况返回,你前端不做代码健壮性处理?而且即使我返回的是null,与下面渲染逻辑有什么关系吗? 你该渲染还是渲染啊!
听到大佬这么回答,顿时我与测试都陷入了沉默。(JS的报错确实会打断后续逻辑的交付,大佬这么讲也没什么毛病)
测试:火锅哥,你改一下吧! 不然老板怪我没测出来,帮你点杯蜜雪呗!
我:行吧行吧~ 反正加一行判断的事~

改完准备发版的时候,后端大佬又来了一句,不只这里可能会返回null,其他的接口你也注意看一下哦!
听到大佬的这话,我彷佛天都要塌~ 那我的这个工作量太大了!每个都要去加判断呀,由于当时项目急着上线,很多这种接口的地方,都没做容错处理。
想到以后所有的项目都是找大佬对接接口,如果每次写代码都这样写,或者有稍微一不注意漏掉的地方,下次测试就直接给我提BUG了,这点逼绩效全部都要扣完。
于是乎我决定造一个轮子工具,针对处理数组的轮子工具,即使类型不是数组,也不能出现报错打断的情况。让逻辑继续走,顶多报一个警告提示出来,后续再处理!
safe-array-utils诞生,源码如下:
js
class SafeArrayWrapper {
#array;
constructor(input, error) {
let array = [];
const rawType = Object.prototype.toString.call(input);
//首先做类型的判断:
//1.如果是数组就不管,直接赋值
//2.如果是类数组,就直接转换成数组
//3.如果是其他类型,就直接抛警告
//4.最后不是数组类型的数据,全部赋值为空数组
if (Array.isArray(input)) {
array = input;
} else if (
rawType === '[object Arguments]' ||
(typeof input === 'object' && input !== null && 'length' in input)
) {
try {
array = Array.from(input);
} catch (e) {
console.warn(`[SafeArray] 类数组转换失败`);
}
} else {
console.warn(`[SafeArray] 输入不是合法数组或类数组。类型:${rawType},定位标记:${error || '无'}`);
}
this.#array = array;
}
//下面就是针对数组原型上面一些方法的操作了,保留原数组原型方法的特性
static chainableMethods = ['map', 'filter', 'slice', 'concat', 'flat', 'flatMap', 'reverse', 'sort'];
static terminalMethods = [
'join', 'reduce', 'find', 'findIndex', 'includes',
'indexOf', 'lastIndexOf', 'every', 'some', 'at',
'toString', 'toLocaleString'
];
static allowedMethods = [...this.chainableMethods, ...this.terminalMethods, 'value'];
static #proxyMethod(methodName) {
return function (...args) {
const target = this.#array;
if (typeof target[methodName] !== 'function') {
console.error(`数组不存在名为 "${methodName}" 的方法`);
return this;
}
const result = Array.prototype[methodName].apply(target, args);
if (SafeArrayWrapper.chainableMethods.includes(methodName)) {
return new SafeArrayWrapper(result).#array;
} else {
return result;
}
};
}
value() {
return [...this.#array];
}
static {
for (const method of this.allowedMethods) {
this.prototype[method] = this.#proxyMethod(method);
}
}
}
function SafeArray(input, error) {
return new SafeArrayWrapper(input, error);
}
export default SafeArray;
其实整个轮子的核心逻辑就是,类型是数组就还是走数组方法的操作,类型不是数组就抛出警告,同时把传入的值赋值为空数组。
同时为了使用方便,我把它打成了一个npm包,包名就叫:safe-array-utils
1.正常用法:
js
import SafeArray from 'safe-array-utils'
let arr = [1,2,3,4]
//与使用数组方法一样,只是把数组包裹一层函数调用
SafeArray(arr).forEach(el=>{
console.log(el)
})
let newArr = SafeArray(arr).map(el=>{
return el * 2
})
//同时也支持链式调用
let arr2 = [1,2,3,4,undefined]
let newArr = SafeArray(arr2).filter(_=>_).map(el=>{
return el * 2
})
//包括其它的数组方法,如
let arr2 = [1,2,3,4]
SafeArray(arr2).at(1)
SafeArray(arr2).join('-')
2.如果传入错误类型
js
//即使传的类型不对,也不会报错了。更不会打断下面的逻辑
let newArr = SafeArray('null').map(el=>{
return el * 2
})
console.log(newArr) // 空数组 []
console.log('正常打印')

3.假如页面多处地方用到
js
//可以传第二个参数,加一个标记定位
SafeArray('null','第一处').map(el => {
return el * 2
})
SafeArray('null','第二处').map(el => {
return el * 2
})
SafeArray('null','第三处').map(el => {
return el * 2
})

怎么样,兄弟们~ 没有什么事是不能用造1个轮子解决的。如果有,那就造2个!
总结:
还是回到最初的问题,即使后端没按照数组的方式返回结果,我们也能正常走后续的逻辑,从而不会因为页面报错,影响页面上的其他功能。最后我想请问家人们你们平时写代码会注重代码的健壮性吗? 我想如果是项目工期短,时间紧。会很少有人关注这一块,甚至现在还有很多小公司不会去做单元测试。如果该文章对你有帮助,就请点赞+收藏吧!如果你们项目中也遇到这种情况。你会使用哪种方式去做呢?欢迎留言讨论~