原因是因為:
vue
不能檢測data
中數組的變動,如利用索引直接改變一個項的值的時候,利用arr.length修改數組的長度的時候, 還有由於vue2.0 使用的是object.definepropoty進行的數據監聽,導致Vue不能檢測對象屬性的添加和刪除。
解決方法:
Vue.set() 響應式新增與修改數據
此時我們需要知道Vue.set()需要哪些參數,官方API:Vue.set()
調用方法:Vue.set( target, key, value )
target:要更改的數據源(可以是對象或者數組)
key:要更改的具體數據
value :重新賦的值
首先我們來看看vue2.0的響應式原理
目前瀏覽監測對象的變化的方式有Object.defineProperty和ES6的Proxy兩種,在設計vue2.0的時候Proxy在瀏覽器的支持還並不是特別的友好,因此vue2.0是基於Object.defineProperty來實現的
Object.defineProperty(obj, prop, descriptor)
- obj:要在其上定義屬性的對象
- prop:要定義或修改的屬性的名稱
- descriptor:將被定義或修改的屬性描述符
Vue
框架內部大量使用了此API為對象定義屬性,其響應式原理也是通過此API自定義setter與getter而完成的。
ECMAScript有兩種屬性
- 數據屬性[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]
- 訪問器屬性[[Configurable]]、[[Enumerable]]、[[Get]]、[[Set]]
訪問器屬性包含兩個函數get,和set,在讀取訪問器屬性的時候,會調用getter函數,這個函數負責返回有效的值,在寫入訪問器屬性的時候會調用setter函數冰川乳新的值,這個函數負責決定如何處理數據訪問器。
var obj = {}; var a; Object.defineProperty(obj, 'a', { get: function() { console.log('get a val'); return a; }, set: function(newVal) { console.log('set a val:' + newVal); a = newVal; } }); obj.a; // get a val obj.a = '111'; // set a val: 111
然后是觀察者模式
vue是基於觀察者模式來實現數據更新之后觸發一系列的相關依賴來自動更新視圖。那么先來了解一下什么是觀察者模式,觀察者模式是指一個對象維持一系列的依賴於他的對象,將有關狀態變更自動的通知給他們。 觀察者模式的基本要素
- Subject (目標)
- Observer (觀察者)
定義一個收集所有依賴的容器
Watcher是一個中介的角色,數據發生變化時通知它,然后它再通知其他地方。 他就是負責具體的臟活累活
- 1、收集依賴
- 2、負責執行cb來更新所有的依賴
總結
vue如何實現響應式?具體實現上對象和數組稍有不同:
- 1、對象:在create階段,會遞歸的將data中的數據遞歸的添加get、set訪問器屬性,頁面在mount階段會創建全局的Watcher,並且mount階段需要執行render渲染,會調用頁面數據對應的get函數,每個數據的key都有對應的dep依賴,執行dep.depend()時會將 將dep 添加至當前watcher的subs隊列中去。當頁面數據更新后,調用set函數,執行通知。
- 2、數組:在create階段,如果是數組類型時,給會執行數組改變方法添加攔截器,同時也會給數據添加get和set訪問器屬性,只是數組改變時並不會觸發set函數,頁面在mount階段執行render,調用數據對應的get函數,並調用childObj.dep.depend()收集watcher,(childObj.dep是什么?在初始化的data的時候會遞歸的將array轉成observer,所以childObj.dep指的是數組array的依賴)。在array數據更新之后,會執行攔截器中的__obj__.dep.notify()執行通知,set並不會觸發。
通知之后頁面怎么更新渲染? 當發送通知之后,會將watcher添加至隊列中由vue統一調度執行更新,后期vue將會進行patch,對比虛擬dom,以當前頁面組件級別做一個整體更新。
鏈接:https://juejin.cn/post/6854573221526634509