問題: 在使用 vue 進行開發的過程當中,可能會遇到一種狀況:當生成vue實例後,再次給數據賦值時,有時候並不會自動更新到視圖上去。也就是 若是在實例建立以後添加新的屬性到實例上,它不會觸發視圖更新。javascript
案例:css
<template> <div class="home"> <div>{{student}}</div> <div v-for="(item,index) in items" :key="index">{{item}}</div> <button @click="btn()">修改</button> </div> </template> <script> export default { name: 'Home', data(){ return{ student:{ name:'張三', }, items:[1, 2, 3], } }, methods:{ btn(){ this.student.age = 18; this.items[1] = 'two'; console.log(this.student,this.items); } } } </script>
當點擊按鈕後頁面:
vue
當點擊按鈕後控制檯:
java
緣由:
受 ES5 的限制,Vue.js 不能檢測到對象屬性的添加或刪除。由於 Vue.js 在初始化實例時將屬性轉爲 getter/setter,因此 屬性必須在 data 對象上才能讓 Vue.js 轉換它,才能讓它是響應的。api
所以:
Vue 不能檢測如下變更的數組:
當你利用索引直接設置一個項時,例如:vm.items[indexOfItem] = newValue
當你修改數組的長度時,例如:vm.items.length = newLength
eg:
使用 this.arr[0] 去更新 array 的內容,視圖沒有刷新
使用 Vue.set(this.arr, 0, !this.arr[0]) 去更新 array 的內容,視圖被刷新
使用 this.arr[0] = !this.arr[0] 和 this.obj.a = !this.obj.a 同時更新,視圖被刷新數組
結論:
若是方法裏面單純的更新數組 Array 的話,要使用 Vue.set();
若是方法裏面同時有數組和對象的更新,直接操做 data 便可;ide
每一個組件實例都有相應的 watcher 實例對象,它會在組件渲染的過程當中把屬性記錄爲依賴,以後當依賴項的 setter 被調用時,會通知 watcher 從新計算,從而導致它關聯的組件得以更新。
受現代 JavaScript 的限制 (並且 Object.observe 也已經被廢棄),Vue 不能檢測到對象屬性的添加或刪除。因爲 Vue 會在初始化實例時對屬性執行 getter/setter 轉化過程,因此屬性必須在 data 對象上存在才能讓 Vue 轉換它,這樣才能讓它是響應的。函數
3.1 經過 Vue.set() 改寫
語法:this
Vue.set( target, propertyName/index, value ) 參數: {Object | Array} target {string | number} propertyName/index {any} value 返回值:設置的值。 用法: 向響應式對象中添加一個 property,並確保這個新 property 一樣是響應式的,且觸發視圖更新。 它必須用於向響應式對象上添加新 property,由於 Vue 沒法探測普通的新增 property (好比 this.myObject.newProperty = 'hi') 注意: 對象不能是 Vue 實例,或者 Vue 實例的根數據對象。
<template> <div class="home"> <div>{{student}}</div> <div v-for="(item,index) in items" :key="index">{{item}}</div> <button @click="btn()">修改</button> </div> </template> <script> import Vue from 'vue' // 別忘了引入 export default { name: 'Home', data(){ return{ student:{ name:'張三', }, items:[1, 2, 3], } }, methods:{ btn(){ Vue.set(this.student, 'age', 18); Vue.set(this.items, 1, 'two'); console.log(this.student,this.items); } } } </script>
當點擊按鈕後頁面:
當點擊按鈕後控制檯:
spa
3.2 經過 $set() 改寫
語法:
vm.$set( target, propertyName/index, value ) 參數: {Object | Array} target {string | number} propertyName/index {any} value 返回值:設置的值。 用法: 這是全局 Vue.set 的別名。 參考:Vue.set
<template> <div class="home"> <div>{{student}}</div> <div v-for="(item,index) in items" :key="index">{{item}}</div> <button @click="btn()">修改</button> </div> </template> <script> export default { name: 'Home', data(){ return{ student:{ name:'張三', }, items:[1, 2, 3], } }, methods:{ btn(){ this.$set(this.student, 'age', 18); this.$set(this.items, 1, 'two'); console.log(this.student,this.items); } } } </script>
當點擊按鈕後頁面:
當點擊按鈕後控制檯:
3.3 Vue.set() 和 this.$set() 的區別
Vue.set() 源碼:
import { set } from '../observer/index' ... Vue.set = set ...
this.$set() 源碼
import { set } from '../observer/index' ... Vue.prototype.$set = set ...
能夠發現 Vue.set() 和 this.$set() 這兩個 api 的實現原理基本如出一轍,都是使用了set函數。 set 函數是從 …/observer/index 文件中導出的。 區別: Vue.set( ) 是將 set 函數綁定在 Vue 構造函數上,this.$set() 是將 set 函數綁定在 Vue原型上。