平时做项目时会经常对数组和对象进行数据更新操作,而有时数据并没有及时更新,这时我们会用Vue.set(),this.$set()等方法让数据及时更新。
<body>
<div id="app">
obj:{{obj}}
<ul>
<li v-for="(item,index) in arr" v-bind:key="item">
arr[{{index}}]:{{item}}
</li>
</ul>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el:"#app",
data:{
arr:[1,2],
obj:{
a:3
}
}
})
</script>
此时页面显示的数据如下图:
添加以下代码,发现数据并没有改变:
vm.$data.arr[0] = 10; //页面不会重新渲染
vm.$data.obj.b = 15; //页面不会重新渲染
而添加以下代码,数据可以改变:
vm.$data.obj.a = 20; //页面可以重新渲染
这是因为受现代 JavaScript 的限制,Vue 无法检测到对象属性的添加或删除。
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
而vm.$data.arr[0] = 10之所以不会重新渲染,是因为vue2在处理数组响应式变化时,采用覆盖 push、pop、shift、unshift、splice、reverse、sort 这七个数组方法,并将其处理为可以发送更新通知的函数实现的,故不能直接修改对应下标的值。
//vm.$data.obj.a 是响应式的
//vm.$data.obj.b 是非响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性。
Vue.set(vm.$data.arr,0,10) //页面可以重新渲染
Vue.set(vm.$data.obj,'b',15) //页面可以重新渲染
还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名
vm.$set(vm.$data.arr,0,10) //页面可以重新渲染
vm.$set(vm.$data.obj,'b',15) //页面可以重新渲染
Vue 在更新 DOM 时是异步执行的。
Vue.nextTick()是基于更新后的DOM状态进行操作。
例如:
<body>
<div id="app">{{message}}</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: '123'
}
})
</script>
页面显示为:123。
添加以下代码,更改数据:
vm.message = 'new message' // 更改数据
页面显示为:new message。但该组件不会立即重新渲染。
为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
将打印结果进行对比:
console.log(vm.$el.textContent === 'new message') // false
此时DOM更新还未完成,vm.$el.textContent===‘123’。
Vue.nextTick(function () {
console.log(vm.$el.textContent === 'new message') // true
})
使用Vue.nextTick(callback),回调函数在 DOM 更新完成后被调用,即此时DOM更新已完成,vm.$el.textContent === ‘new message’。
在组件内使用 vm.$nextTick() 实例方法特别方便,因为它不需要全局 Vue,并且回调函数中的 this 将自动绑定到当前的 Vue 实例上:
Vue.component('example', {
template: '{{ message }}',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
2.1.0 起新增:如果没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,所以如果你的目标浏览器不原生支持 Promise (IE:你们都看我干嘛),你得自己提供 polyfill。
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 作为一个 Promise 使用
Vue.nextTick()
.then(function () {
// DOM 更新了
})
可见Vue深入响应式原理https://cn.vuejs.org/v2/guide/reactivity.html