记一次前端幽灵bug的解决
写在前面
笔者在微信小程序开发具备较多的经验,从原生开发到uniapp,以及Taro均有涉猎。
在一个项目业务逻辑中,每次重进小程序将自动登录最近登录过的账号,同时进入登录页面显示历史账号。
然而,近期出现一个奇怪的bug:小程序无法自动登录,历史账号也消失了。
更令人匪夷所思的是,笔者在开发者工具和手机上反复测试,并未发现任何bug。这bug如同幽灵一般,随机的出现。
经过广泛测试,事实上,该bug在一部分人的手机上出现,但在另一部分人手机上完全正常。
排查过程
由于笔者调试时并未发现任何问题,并且以往也从未出现该bug,竟然有种无从下手的尴尬。
以下为寻找解决方案的经过。
-
尝试将代码复原至上一个版本 (×)
首先,我将最新的改动还原,试图解决该问题。然而该问题仍然存在,令人费解。
-
在出问题的手机上启动“开发调试”(√)
尽管程序看起来逻辑完全正确,但按照常理,一定是有报错阻碍了相关内容的呈现。
在出现bug的手机上启动小程序“开发调试”,找到了“报错”,如图所示。
解决方法:
经过分析报错,定位到一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
onLoad() {
this.accountList = uni.getStorageSync('accountList') || [] // (1)
if (this.accountList.length) {
const [arr1, arr2] = this.accountList.reduce((result, item) => {
item.identity = Number(item.identity)
// 根据 identity 将对象分配到不同的数组
result[item.identity - 1].push(item) // identity取值为1:家长 2:教师
return result
}, [[], []]); // 初始化为两个空数组
this.parentList = arr1
this.teacherList = arr2
}
},
|
意味着result[item.identity - 1]为undefined。进一步调试,item.identity也未定义。
而存储storage的代码为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
export function saveLoginInfo(identity, name, password) {
// 从本地存储中获取账号列表
let accountList = uni.getStorageSync('accountList') || [] // (1)
// 检查账号是否已经存在
let accountExists = accountList.some(account => account.name === name)
uni.setStorageSync("name", name)
uni.setStorageSync("password", password)
if (!accountExists) {
// 创建当前登录的账号信息对象
let newAccount = { identity: identity, name: name, password: password }
// 将新账号信息添加到账号列表中
accountList.push(newAccount)
// 将更新后的账号列表保存回本地存储
uni.setStorageSync('accountList', accountList) // (2)
} else {
console.log('账号已存在,未重复添加')
}
}
|
因此accountList应该呈现如
1
|
Array [Object {identity: 1", name:"xxx", password: "xxxxxx"}, ...]
|
但打印调试信息发现,accountList居然出现了信息丢失,形如
1
|
Array [Object {name:"xxx", password: "xxxxxx"}, ...]
|
identity属性直接消失了!
罪魁祸首在于存储本地缓存时未将对象数组利用JSON转变成字符串。
修改方法
1
2
3
4
5
6
7
8
9
|
// 将(1)修改为:
let accountList = [];
let accountStr = uni.getStorageSync('accountList')
if (accountStr) {
accountList = JSON.parse(accountStr)
}
// 将(2)修改为:
uni.setStorageSync('accountList', JSON.stringify(accountList));
|
大功告成! 针对使用过的用户,还需删除原来的缓存。因为原来的缓存中直接存了对象数组,无法读取。
将小程序删除后重新进入即可。
总结
经过这次“幽灵”bug的排查与修复,我深感前端的代码务必落实规范性,有些写法看似能用,其实存在极大的隐患,而且不易发现。存取storage时,如遇对象/对象数组,使用JSON.stringify()和JSON.parse()这一套,切勿贪图方便,埋下隐患。
另外,不要过分相信模拟器,务必在多台手机(安卓、苹果)上进行广泛测试,使用开发调试工具来排查bug。