Vue數據更新頁面沒有更新問題總結
1. Vue無法檢測實例別創建時不存在於data
中的property
- 原因: 由於Vue會在初始化實例時對property執行
getter/setter
轉化,所以property
必須在data
對象上存在才能讓vue轉換為響應式
// 問題
var vm = new Vue({
data: {},
template: '<div>{{item}}</div>'
})
vm.item = 'hello world!!!'
// 解決
var vm = new Vue({
data: {
item: '', // 聲明
},
template: '<div>{{item}}</div>'
})
vm.item = 'hello world!!!'
2. Vue無法檢測對象property的添加或者刪除
- 原因: 這是由於ECMAJavaScript 5 的限制,vue.js
不能檢測到對象屬性的添加或者刪除
.
// 問題
var vm = new Vue({
data: {
obj: {
id: 1,
},
},
template: '<div>{{obj.item}}</div>' // 沒有變化
})
vm.obj.item = 'hello world!!!'
delete vm.obj.id
// 解決
// 動態添加
Vue.set(vm.obj, propertyName, newValue)
Vue.$set(vm.obj, propertyName, newValue)
// 動態添加多個
this.obj = Object.assign({}, this.obj, {a: 1, b: 2})
// 動態刪除
Vue.delete(vm.obj, propertyName)
Vue.$delete(vm.obj, propertyName)
3. Vue不能檢測通過數組索引直接修改一個數組項,也不能檢測直接修改數組長度的變化
- 原因: 官方,由於js的限制,Vue不能檢測數組和對象的變化,尤雨溪-性能代價和獲得用戶體驗不成正比
// 問題
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 這不是響應性的
vm.items.length = 4 // 不是響應的
// 解決
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
// 解決長度
vm.items.splice(newLength)
拓展:Object.defineProperty() 可以監測數組的變化
- Object.defineProperty() 可以監測數組的變化。但對數組新增一個屬性是不會監測到數據變化,因為無法監測到新增數組的下標,刪除一個屬性也是。
var arr = [1, 2, 3, 4]
arr.forEach(function(item, index) {
Object.defineProperty(arr, index, {
set: function(value) {
console.log('觸發 setter')
item = value
},
get: function() {
console.log('觸發 getter')
return item
}
})
})
arr[1] = '123' // 觸發 setter
arr[1] // 觸發 getter 返回值為 "123"
arr[5] = 5 // 不會觸發 setter 和 getter
4. 在異步更新執行之前操作 DOM 數據不會變化
- 原因: Vue在更新DOM時是異步執行,只要偵聽到數據變化,Vue將開啟一個隊列.並緩沖在統一事件循環中發生的所有數據變更,如果同一個watcher被多次觸發,只會別推入到隊列中一次!這種在緩沖時去除重復數據對於避免不必要的計算和DOM操作是非常重要的.然后在下一個事件循環
nextTick
中,Vue刷新隊列並執行實際工作,Vue 在內部對異步隊列嘗試使用原生的Promise.then``、MutationObserver
和setImmediate
,如果執行環境不支持,則會采用setTimeout(fn, 0)
代替。
<div id="box">{{item}}</div>
var vm = new Vue({
el: '#box',
data: {
item: 'abc'
},
})
vm.item = 'ABC' // 數據改變
vm.$el.textContent === 'ABC' // 返回false
vm.$el.style.color = 'red' // 沒有變化
// 解決
Vue.nextTick(function () {
vm.$el.textContent === 'ABC'
vm.$el.style.color = 'red'
})
5. 路由參數變化時,頁面不更新,也就是數據不更新
- 原因: 路由視圖組件引用了相同組件時,當路由參會變化時,會導致該組件無法更新,也就是我們常說的頁面無法更新的問題
<div id="app">
<ul>
<li><router-link to="/home/a">To A</router-link></li>
<li><router-link to="/home/b">To B</router-link></li>
<li><router-link to="/home/c">To C</router-link></li>
</ul>
<router-view></router-view>
</div>
const Home = {
template: `<div>{{item}}</div>`,
data() {
return {
item: this.$route.params.name
}
}
}
const router = new VueRouter({
mode:'history',
routes: [
{path: '/home', component: Home },
{path: '/home/:name', component: Home }
]
})
new Vue({
el: '#app',
router
})
- 這段代碼中,在路由構建選項
router
中配置了一個動態路由/home/:name
,它們共用一個路由組件.當路由切換時,頁面只會渲染第一次匹配到的參數,之后再進行路由切換,item是沒有變化的
// 解決
// 通過watch監聽$route的變化
const Home = {
template: `<div>{{item}}</div>`,
data() {
return {
item: this.$route.params.name
}
},
watch: {
'$route': function() {
this.item = this.$route.params.name
}
}
}
...
new Vue({
el: '#app',
router
})
異步更新帶來的數據響應的誤解參考 - https://github.com/xiaofuzi/deep-in-vue/issues/11