当前位置: 首页 > 科技观察

Vue数据更新了但页面没有更新的 7 种情况汇总及延伸

时间:2023-03-11 20:16:03 科技观察

Vue数据更新但页面没有更新的7种情况的总结和扩展,所以属性必须存在于数据对象上,Vue才能将其转换为响应式。Scene:varvm=newVue({data:{},//页面不会变化template:'

{{message}}
'})vm.message='Hello!'//`vm.message`不是响应式解决方案:varvm=newVue({data:{//declareaandbareanemptystringmessage:'',},template:'
{{message}}
'})vm.message='你好!'2.Vue无法检测对象属性的增减原因:官方-由于JavaScript(ES5)的限制,Vue.js无法检测对象属性的增减。因为Vue.js在初始化实例时会将属性转换为getters/setters,所以属性必须在数据对象上,以便Vue.js对其进行转换,使其具有响应性。Scene:varvm=newVue({data:{obj:{id:001}},//页面不会变化template:'
{{obj.message}}
'})vm.obj.message='hello'//Notresponsivedeletevm.obj.id//不是无响应解决方案://Dynamicadd-Vue.setVue.set(vm.obj,propertyName,newValue)//Dynamicadd-vm.$setvm.$set(vm.obj,propertyName,newValue)//动态添加多个//替换`Object.assign(this.obj,{a:1,b:2})`this.obj=Object.assign({},this.obj,{a:1,b:2})//动态移除-Vue.deleteVue.delete(vm.obj,propertyName)//动态移除-vm.$deletevm.$delete(vm.obj,propertyName)3.Vue无法检测到使用数组索引直接修改数组项原因:官方-由于JavaScript的限制,Vue无法检测到数组和对象的变化;优玉玺——性能成本与用户体验不成正比。场景:varvm=newVue({data:{items:['a','b','c']}})vm.items[1]='x'//不是响应式解决方案://Vue.setVue.set(vm.items,indexOfItem,newValue)//vm.$setvm.$set(vm.items,indexOfItem,newValue)//Array.prototype.splicevm.items.splice(indexOfItem,1,newValue)扩展名:对象.defineProperty()可以监听到数组的变化,但是给数组增加一个属性(索引)是检测不到数据变化的,因为新加入的数组的下标(索引)是监听不到的,对于删除属性(索引)。场景:vararr=[1,2,3,4]arr.forEach(function(item,index){Object.defineProperty(arr,index,{set:function(value){console.log('triggersetter')item=value},get:function(){console.log('triggergetter')returnitem}})})arr[1]='123'//triggersetterarr[1]//triggergetter返回值为"123"arr[5]=5//不会触发setter和getter4.Vue无法监听直接修改数组长度的变化原因:官方-由于JavaScript的限制,Vue无法检测到数组和对象的变化;游鱼溪——性能成本与获得的用户体验是不成正比的。(Object.defineProperty()可以监听数据变化)Scene:varvm=newVue({data:{items:['a','b','c']}})vm.items.length=2//无响应解决方案:vm.items.splice(newLength)5.执行异步更新前操作DOM数据不会改变原因:Vue在更新DOM时是异步执行的。只要检测到数据变化,Vue就会打开一个队列,缓冲所有发生在同一个事件循环中的数据变化。如果同一个观察者被多次触发,它只会被推入队列一次。这种缓冲时的重复数据删除对于避免不必要的计算和DOM操作非常重要。然后,在下一个事件循环“tick”中,Vue刷新队列并执行实际(去重)工作。Vue内部尝试使用原生的Promise.then、MutationObserver和setImmediate来实现异步队列。如果执行环境不支持,它将使用setTimeout(fn,0)代替。场景:{{message}}
varvm=newVue({el:'#example',data:{message:'123'}})vm.message='newmessage'//改变数据vm.$el.textContent==='newmessage'//falsevm.$el.style.color='red'//页面不改变解决方法:varvm=newVue({el:'#example',data:{message:'123'}})vm.message='newmessage'//更改数据//使用Vue.nextTick(callback)DOM更新完成后会调用回调Vue.nextTick(function(){vm.$el.textContent==='newmessage'//truevm.$el.style.color='red'//文字颜色变成红色})展开:异步更新导致的数据响应的误区{{message.text}}
varvm=newVue({el:'#example',data:{message:{},}})vm.$nextTick(函数(){this.message={}this.message.text='我已经更新了!'})在上面的代码中,我们在数据对象中声明了一个空的消息对象,然后在结束后异步触发nextDOMupdatecycle在回调中,执行了下面两段代码:this.message={};this.message.text='我正在更新!'这里,模板已经更新,页面最后会显示我正在更新!。模板已经更新,应该是响应式的,如果你这么认为那你就进入误区了。一开始我们只是在data对象中声明了一个空的message对象,没有text属性,所以text属性是没有响应的。但是模板居然已经更新了,这是怎么回事呢?那是因为Vue.js的DOM更新是异步的,即当setter操作发生时,指令不会立即更新,指令的更新操作会有一个延迟,当指令更新真正执行时,text属性此时已经赋值,所以指令在更新模板时获取新值。模板中的每个指令/数据绑定都有一个相应的观察者对象,该对象在评估期间将属性记录为依赖项。之后调用依赖的setter时,会触发watcher重新计算,也会导致其关联指令更新DOM。具体过程如下:当this.dataObj={};被执行时,setter被调用。Vue.js跟踪消息依赖的setter被调用后,会触发watcher重新计算。this.message.text='新文本';为文本属性分配一个值。异步回调逻辑执行完成后,会导致其关联指令更新DOM,指令更新开始执行。所以真正触发模板更新的操作是this.message={};这句话造成的,因为触发了setter,所以就看上面的例子,具有响应式特性的数据只是消息层,其动态添加的属性是不可用的。对应上面第二点——Vue无法检测对象属性的增减6.循环嵌套层次太深,视图不更新?网上有人说数据更新层级太深,导致数据不更新或者更新慢。是因为不想更新吗?由于我没有遇到过这种情况,当我尝试重现这个场景时,发现并没有出现上面的情况,所以对此我就不过多描述了(如果有人在真实场景中遇到这种情况请告知).有人针对上述情况给出的解决方案是使用强制更新:如果你发现自己需要在Vue中进行强制更新,99.9%的情况下,你在某个地方做错了。vm.$forceUpdate()7.扩展:当路由参数改变时,页面不更新(数据不更新)。展开一个因为路由参数改变导致页面不更新的问题。页面不更新的本质是数据没有更新。原因:当路由视图组件引用同一个组件时,当路由参与发生变化时,组件无法更新,也就是我们常说的页面无法更新的问题。场景:
  • ToFoo
  • ToBaz
  • ToBar
constHome={template:`
{{message}}
`,data(){return{message:this.$route.params.name}}}constrouter=newVueRouter({mode:'history',routes:[{path:'/home',component:Home},{path:'/home/:name',component:Home}]})newVue({el:'#app',router})在上面的代码中,我们在路由构建选项routes中配置了一个动态路由'/home/:name',它们共享一个路由组件Home,也就是复用了RouterView。路由切换时,页面只会渲染第一个路由匹配的参数,后面切换路由时消息不会改变。解决方法:解决方法很多,这里只介绍我常用的一种方法。通过watch监控$route的变化。constHome={template:`
{{message}}
`,data(){return{message:this.$route.params.name}},watch:{'$route':function(){thisthis.message=this.$route.params.name}}}...newVue({el:'#app',router})绑定了key属性,这样Vue会认为是不一样的。缺点:如果从/home跳转到/user等路由,我们不用担心组件更新,所以此时key属性是多余的。...