vue数据更改视图不更新问题----深入响应式原理
当你把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器。
这些getter/setter对用户来说是不可见的,但是在内部他们让vue追踪依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的watcher实例对象,它会在组建渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
对象更改检测
在一个组件实例中,只有在data里初始化的数据才是响应的,Vue不能检测到对象属性的添加或删除,没有在data里声明的属性不是响应的。
Vue不允许在已经创建的实例上动态添加根级响应式属性,但是可以使用$set方法将相应属性添加到嵌套的对象上。
例子一:属性a是已经声明了的,是响应的;属性b没有提前声明,因此改变了属性b的值的时候,如果直接赋值 this.form.b = 'desc' , 虽然打印出来的值是改变了的,但是视图并没有更新。需要用Vue.set(object, key, value) 【或 this.$set(object,key,value)】 方法将响应属性添加到嵌套的对象上。注意:object 必须是在data中已经声明的对象。
...... <section> <p>动态添加嵌套属性1:</p> <p>a的值{{form.a}}</p> <p>b的值{{form.b}}</p> <el-button @click="changeA">改变A</el-button> <el-button @click="changeB">改变B</el-button> </section>
....... export default { name: "layout", data(){ return { form:{ a:''//初始化 } } }, methods:{ changeA(){ this.form.a = 1 }, changeB(){
//this.form.b = 'desc' //无效 this.$set(this.form,'b','desc')//动态添加
//或者 Vue.set(vm.form,'b','desc') } } } </script>
例子二:Vue.set(object,key,value)方法一次只能添加一个属性,如果需要向嵌套对象上添加多个属性,可以用Object.assign方法。object.assign方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,并返回目标对象。
vm.object = Object.assign( { } , vm.object , {a:' 1 ', b:' 2 ' }) 注:object必须是已经声明的对象
...... <section> <p>object.assign方法</p> <p>{{form3.f1}}</p> <p>{{form3.f2}}</p> <el-button @click="changeForm3">添加多个属性</el-button> </section> ...... export default { name: "layout", data(){ return { form3:{} } }, methods:{ changeForm3(){ this.form3 = Object.assign({},this.form3,{f1:'f1',f2:'f2'}) } } }
数组渲染问题
通过改动两种方式改动数组时,Vue检测不到变动:1.利用索引直接设置一个项;2.修改数组长度。
例子三:利用索引直接设置一个项,不能直接触发状态更新。
<p v-for="(item,index) in trees" @click="changeMe(index)">这个是{{item}}</p> export default { name: "layout", data(){ return { trees:['a','b','c'] } }, methods:{ changeMe(idx){ //this.trees[idx] = 'x'//无响应 this.$set(this.trees,idx,'x')//有响应
//this.trees.splice(idx,1,'x')//有响应
}
}
}
例子四:改变数组长度
<p v-for="(item,index) in trees">这个是{{item}}</p> <el-button @click="changeLength">改变长度</el-button> export default { name: "layout", data(){ return { trees:['a','b','c'] } }, methods:{ changeLength(){ //this.trees.length = 2//无响应 this.trees.splice(2)//有响应 } } }