關於vue子組件的數據變了視圖不更新的解決辦法


原因是因為:   

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:將被定義或修改的屬性描述符
Object.defineProperty()是es5新增的方法,它的作用是可以通過該API直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。 Vue框架內部大量使用了此API為對象定義屬性,其響應式原理也是通過此API自定義setter與getter而完成的。

ECMAScript有兩種屬性

  1. 數據屬性[[Configurable]]、[[Enumerable]]、[[Writable]]、[[Value]]
  2. 訪問器屬性[[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
所以Object.defineProperty 把obj 的 a 屬性轉化為 getter 和 setter,可以實現 obj.a 的數據監控,Vue正式基於這個特性實現了響應式。 Vue 會遍歷對象所有的 property,並使用 Object.defineProperty 把這些 property 全部轉為 getter/setter
由於javascript的限制,Object.defineProperty()不能監測到數組的改變,vue對數組和對象使用了2種不同的方式實現,對於Object類型來說,通過劫持getter和setter來實現監測改變;對於Array來說,通過攔截器,攔截數組相關api(push、pop、shift、unshift...)來實現監測改變。所以在這里我們可以發現通過數組的下標去改變數組的某一項或者直接改變數組的長度的時候因為vue沒有做這方面的處理而Object.defineProperty又監聽不到所以vue是無法對此進行監聽的
 

然后是觀察者模式

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM