vue深入響應式原理的理解及筆記

利用索引直接設置一個數組項,直接修改數組的長度,對象屬性的添加等等問題,視圖不更新,感到十分奇怪,讀了響應式原理許多問題豁然開朗

Vue 最獨特的特性之一,是其非侵入性的響應式系統。數據模型僅僅是普通的 JavaScript 對象。而當你修改它們時,視圖會進行更新。這使得狀態管理非常簡單直接,不過理解其工作原理同樣重要,這樣你可以避開一些常見的問題。

☆知識點:Vue 不支持 IE8 以及更低版本瀏覽器的原因 ☆

 當你把一個普通的 JavaScript 對象傳入 Vue 實例作爲 data 選項,Vue 將遍歷此對象所有的屬性,並使用 Object.defineProperty 把這些屬性全部轉爲 getter/setterObject.defineProperty 是 ES5 中一個無法 shim 的特性

☆知識點:數據更新的渲染過程 ☆   

這些 getter/setter 對用戶來說是不可見的,但是在內部它們讓 Vue 能夠追蹤依賴,在屬性被訪問和修改時通知變更。每個組件實例都對應一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數據屬性記錄爲依賴。之後當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的組件重新渲染。

data

☆知識點:數據更新的視圖不更新的常見原因 ☆    

受現代 JavaScript 的限制 (而且 Object.observe 也已經被廢棄),Vue 無法檢測到對象屬性的添加或刪除。由於 Vue 會在初始化實例時對屬性執行 getter/setter 轉化,所以屬性必須在 data 對象上存在才能讓 Vue 將它轉換爲響應式的。例如:

var vm = new Vue({
  data:{
    a:1
  }
})

// `vm.a` 是響應式的

vm.b = 2
// `vm.b` 是非響應式的

對於已經創建的實例,Vue 不允許動態添加根級別的響應式屬性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套對象添加響應式屬性。例如,對於:

Vue.set(vm.someObject, 'b', 2)

您還可以使用 vm.$set 實例方法,這也是全局 Vue.set 方法的別名:

this.$set(this.someObject,'b',2)

有時你可能需要爲已有對象賦值多個新屬性,比如使用 Object.assign() 或 _.extend()。但是,這樣添加到對象上的新屬性不會觸發更新。在這種情況下,你應該用原對象與要混合進去的對象的屬性一起創建一個新的對象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

 ☆知識點: Vue 不允許動態添加根級響應式屬性,所以你必須在初始化實例前聲明所有根級響應式屬性 ☆   

官網例子:

var vm = new Vue({
  data: {
    // 聲明 message 爲一個空值字符串
    message: ''
  },
  template: '<div>{{ message }}</div>'
})
// 之後設置 `message`
vm.message = 'Hello!'

如果你未在 data 選項中聲明 message,Vue 將警告你渲染函數正在試圖訪問不存在的屬性。

這樣的限制在背後是有其技術原因的,它消除了在依賴項跟蹤系統中的一類邊界情況,也使 Vue 實例能更好地配合類型檢查系統工作。但與此同時在代碼可維護性方面也有一點重要的考慮:data 對象就像組件狀態的結構 (schema)。提前聲明所有的響應式屬性,可以讓組件代碼在未來修改或給其他開發人員閱讀時更易於理解。

☆知識點:Vue.nextTick(callback)☆   

Vue 在更新 DOM 時是異步執行的。只要偵聽到數據變化,Vue 將開啓一個隊列,並緩衝在同一事件循環中發生的所有數據變更。如果同一個 watcher 被多次觸發,只會被推入到隊列中一次。這種在緩衝時去除重複數據對於避免不必要的計算和 DOM 操作是非常重要的。然後,在下一個的事件循環“tick”中,Vue 刷新隊列並執行實際 (已去重的) 工作。Vue 在內部對異步隊列嘗試使用原生的 Promise.thenMutationObserver 和 setImmediate,如果執行環境不支持,則會採用 setTimeout(fn, 0) 代替。

例如,當你設置 vm.someData = 'new value',該組件不會立即重新渲染。當刷新隊列時,組件會在下一個事件循環“tick”中更新。多數情況我們不需要關心這個過程,但是如果你想基於更新後的 DOM 狀態來做點什麼,這就可能會有些棘手。雖然 Vue.js 通常鼓勵開發人員使用“數據驅動”的方式思考,避免直接接觸 DOM,但是有時我們必須要這麼做。爲了在數據變化之後等待 Vue 完成更新 DOM,可以在數據變化之後立即使用 Vue.nextTick(callback)。這樣回調函數將在 DOM 更新完成後被調用。例如:

<div id="example">{{message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改數據
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

在組件內使用 vm.$nextTick() 實例方法特別方便,因爲它不需要全局 Vue,並且回調函數中的 this 將自動綁定到當前的 Vue 實例上:

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})

因爲 $nextTick() 返回一個 Promise 對象,所以你可以使用新的 ES2017 async/await 語法完成相同的事情:

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}

官網鏈接:深入響應式原理   https://cn.vuejs.org/v2/guide/reactivity.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章