问题背景
在开发过程中,为了threejs对象在watch监听中能够被顺利取到,我加了一个信号量,在初始化对象后,通过threejs对象状态和表单状态来重新渲染画面。然而,我把threejs对象从null设置为正常的对象时,页面居然卡死了。在实际的代码中,用到对象的情况只有wacth里面监听到信号量为true时。什么都没改,只是把之前的对象赋值页面就没有了
///页面逻辑
let _self = this;
_self.form.x1 = 45;
_self.threejsObj = threejsObj;
_self.useform = true;
///监听逻辑
wacth:
"form.x1":handler:function(newVal,olval){
if(this.useform){
///对threejs对象进行操作,并操作画面
}
}
排查过程
- 第一步,把threejs对象还是设置为null,页面可以正常渲染了
- 第二步,我判断可能是有绑定的数据有问题,我注释了信号量useform,页面也能正常了
- 第三步,我恢复了对象和信号量,去掉了wacth,页面正常了
- 第四步,我加上了日志,看wacth的触发顺序,发现 虽然this.useform在 x1后面赋值但是触发的时候还是true
这样问题就简单了,变量是按顺序赋值的,watch里面也是顺序操作的,可是,数据真正赋值的顺序似乎不对。
问题简单抽象
下面把问题简单描述:
表单里面有x1,x2,y1,y2 同时有useform信号量判断表单是否可用,;在vue的监听里面写如下代码
wacth:
'x1': handler:function(newVal, oldVal){
if(this.useform){
do something;
}
}
在页面逻辑中这样写:
this.x1 = 1;
this.form = true;
发现在给x1赋值的时候,就触发了,并且debugger输出。this.form已经是true,这就奇怪了。
我在这里描述的很简单,实际的代码是很复杂的,表单和页面里面还嵌入了threejs等一系列的变量,这引发了严重的bug.
问题解决
为了确保顺序,我这样加上了代码,给信号量赋值的时候加上nextTick,通过debugger,watch触发逻辑已经正常,页面可以正常渲染了。
this.form.x1 = 45;
this.$nextTick(()=>{this.useform=true})
问题本质
Vue.js框架里面 data绑定的数据,赋值逻辑实际上异步的。也就是说 this.form.x1 = 45; this.useform = true; 在Vue内部看来的顺序,是不一定的。造成情况的原因在于 Vue的双向绑定,data变了dom也要变,实际上Vue在处理的时候是当成队列来处理的。
后记
那么知道了这样的情况,我们在写代码的时候,就要注意了。
1.如果data双向绑定赋值的的顺序,对业务有影响,操作要小心。利用好nextTick;
2.有些数据比如上文的useform如果不参与dom渲染,其实可以不放在data里面