一、问题
1.最近写代码遇到了两次这种同一个接口几乎同时被调用两次情况,决定记录一下。
二、场景及解决方法
1.场景一
父组件Parent给子组件Son传参props1,子组件Son只在初始化时能够正确接收到 子组件传过来的值props1,之后父组件中的props1的值变化,子组件中接收的props1是会进行响应式更改的。
1)但是我的需求是: 在子组件中知道props1变化,就需要 调用函数,进行相应的处理。
我怎么知道 props1什么时候变化了呢?-----watch监听 props1的变化,监听到变化时,做我想做的事情,比如调用函数functionA(里面调用了接口 searchPatient)!!!!!!
2)问题来了:我写好了之后,进入这个页面,发现functionA有时候被调用了两次(接口 searchPatient被调用了两次),并且 props作为接口传参,前后两次调用接口的传参不同,导致两次取到的数据不同。因为 两次调用接口,接口的响应时间不同,异步了,最终导致有时取到的数据是正确的,有时取到的数据是错误的!
3)产生问题的原因: 想了很久都觉得自己没有问题,父组件传过来值,我在子组件中也只接收了一次并且没有修改过 props1的值,怎么就有两次变化?况且其他页面也有类似的操作,也没有调用两次接口呀!!!
最后发现:竟然是因为我在父组件中 给props1赋了两次值。一次是在声明props1变量时赋值为false,一次是在mounted中 因为localStorage中可能存了值,我想判断一下后再给props1赋值。
localStorage中存的是 false的时候,确实只能监听到props1变化了一次;接口只调用一次---没有问题;
localStorage中存的是true的时候,props1从false变成了true,子组件监听懂啊 props1变化了两次,所以接口被调用了两次---有问题。
错误代码如下:
export default {
data() {
return {
// 是否查看所有
searchAll: this.initSearchAll(),
}
},
mounted() {
if (sessionStorage.getItem('isSearchAllPatient')) {
return JSON.parse(sessionStorage.getItem('isSearchAllPatient'));
} else {
sessionStorage.setItem('isSearchAllPatient', false);
return false;
}
}
}
注:如果不是仔细分析,我也以为没有影响的!!!这也间接证明 子组件和父组件 几乎是同步加载的。
上述props1是泛指,具体为下面代码中的 searchAll
4)解决方法:
a.方法一:推荐
在初始化变量 searchAll的时候直接调用一个方法赋值就可以了,不要在mounted中再赋值了!!!
正确代码如下:
export default {
data() {
return {
// 是否查看所有
searchAll: this.initSearchAll(),
}
},
mothods: {
// 初始化仅看我的和查看全部
initSearchAll() {
if (sessionStorage.getItem('isSearchAllPatient')) {
return JSON.parse(sessionStorage.getItem('isSearchAllPatient'));
} else {
sessionStorage.setItem('isSearchAllPatient', false);
return false;
}
},
}
}
b.方法二:
在子组件上加上 v-if=“isShowSon",isShowSon初始化为false等待我在mounted给变量 SearchAll赋值完成后,再加载 子组件Son(isShowSon赋值为true).
不推荐,因为使用了多余的变量,且赋值两次。一次赋值就可以解决的问题,不建议 画蛇添足。
2.场景二
一个页面有很多患者,当我选择了一个患者后,进入改患者对应的页面,存储了改患者的信息currentOperaMsg;当我回到有很多患者的其他页面,我希望不再存储刚才那个患者的信息。想到可以根据路由来判断我到底要在什么时候置空患者的信息。这一切都看上去很合理,没有问题。
1)但是有一个新的需求是:在单个患者对应的页面,可以切换到其他患者,我必须监听currentOperaMsg的变化才能够重新调 页面中的接口;
2)还有另外一个问题:一个患者也对应很多页面我在每个页面都写一个监听到患者变化,然后调用改页面的接口很不合理。----为了简化,想到监听到患者变化时,直接刷新页面,这样只需要在患者所在的每个页面调用一个公共函数reload就可以 实现:切换患者时,改患者对应的所有页面都可以刷新。
3)问题来了:上面的看起来都挺合理的,但是却发现当你从 某个患者所在的页面切换到所有患者的页面时,所有患者的页面接口都被调用了两次。
4)问题产生的原因:路由变化早于组件的销毁,当路由变化时:所有患者的页面被加载一次,同时判断到切换到所有患者的页面,currentOperaMsg被 置为{};单个患者所在的页面还没有被销毁,监听到 currentOperaMsg的变化,执行reload函数,又加载了一次页面。
5)解决方法
真的有这种需求时,可以在除了单个患者及全部患者以外的路由将 currentOperaMsg置为{},因为全部患者页面带有 currentOperaMsg相关的信息应该是不影响业务的。
6)根本的解决方法
上述的需求其实是不合理的,我在除了单个患者的页面拥有 currentOperaMsg理论上不会影响其他页面的。而且对于导航式编程而言,点击浏览器的返回按钮后应该正常 显示之前的页面。如果真的把currentOperaMsg设置为{},在上述场景下,点击 浏览器左上角的 返回按钮后,单个患者页面就没有患者信息了,需要传单个患者信息的接口很可能会报错,如下图所示。这显然是不合理的。
三、总结
1.上述两种情况都和 监听变量有关系。但直接原因不同。
2.同一个接口调用两次可能原因:
a.给一个变量赋值了 两次,监听到两次变化后执行了两次接口;
b.路由变化和手动刷新页面同时进行,导致页面被加载两次,接口被调用两次。
3.写代码注意事项
a.尽可能减少没必要的赋值,保持代码的简洁性。不要随便给变量赋值,赋值的时候要注意初始值的数据类型是否正确。
b.有监听的地方,要仔细考虑一下,是否会影响其他地方。
4.以上是目前遇到的场景,将持续更新!
/*
希望对你有帮助!
如有错误,欢迎指正,非常感谢!
*/