解釋Vue深入響應式原理

weixin_33670713發表於2017-05-01

tip:例項建立之後新增新的屬性到例項上,不會觸發檢視更新

分解tip:new Vue() 生命週期created() 之後 在data內新增的屬性,不會觸發view層的更新

new Vue({{
  el: '#app',
  data: {
    user: {
      name: 'tom',
      sex: 'male'
    }
  },
  created() {
    this.user.msg = 'reactivity'  // 成功建立並在檢視更新
  },
  mounted() {
    this.user.data = 'reactivity' // 成功建立,但不更新檢視
  }
})

解決方法

1Vue API

vm.$set(obj, key, value)

2Object.assign()

Object.assign({}, this.user, {key: value})
new Vue({{
  el: '#app',
  data: {
    user: {
      name: 'tom',
      sex: 'male'
    }
  },
  created() {
    this.user.msg = 'reactivity'  // 成功建立並在檢視更新
  },
  mounted() {
    this.$set(this.user, 'data', 'reactivity') // Vue API
    Object.assign({}, this.user, {'data', 'reactivity'}) // Object.assign
  }
})

非同步資料操作

非同步獲取資料後更新檢視:

new Vue({{
  el: '#app',
  data: {
    user: {
      name: 'tom',
      sex: 'male'
    }
  },
  created() {
     setTimeout(() => {
      // this.user.msg = 'reactivity'  // 不更新檢視
      this.$set(this.user, 'data', 'reactivity') // Vue API
      Object.assign({}, this.user, {'data', 'reactivity'}) // Object.assign
    }, 1000) // 模擬非同步資料
  },
  mounted() {
  }
})

原理:非同步資料操作的棧佇列在整個vue生命週期最後

非同步更新佇列

this.$nextTick() 下一次棧佇列執行完畢之後
非同步資料更新後,操作對應的dom更新

new Vue({{
  el: '#app',
  data: {
    user: {
      name: 'tom',
      sex: 'male'
    }
  },
  created() {
     setTimeout(() => {
       this.user = {'age': 18}
       this.$nextTick(() => {
        // 資料重新整理後對應的dom更新完畢
      })
    }, 1000) // 模擬非同步資料
  },
  mounted() {
  }
})

MutationObserver

vue2.0使用MutationObserver API作為非同步更新佇列的DOM更新解決方案
MutationObserver屬於microtasks,執行效率更高,優先於macrotasks - setTimeout執行

    // Firefox和Chrome早期版本中帶有字首
    const MutationObserver = window.MutationObserver
    || window.WebKitMutationObserver || window.MozMutationObserver
    // 建立觀察者物件
    const observer = new MutationObserver(mutations=>{
      mutations.forEach(function(mutation) {
        console.log(mutation.type);
      })   
    }) 
    // 配置觀察選項:
    const config = { 
      attributes: true, 
      childList: true, 
      characterData: true 
    }
    // 選擇目標節點
    const target = document.querySelector('#test');
    // 傳入目標節點和觀察選項
    observer.observe(target, config);

    target.appendChild(document.createElement("div"))   
    /*
    * mutationObserver優先於setTimeout
    */
    setTimeout(console.log,0,'setTimeout')
    console.log('appending')  
    target.setAttribute('class','hello')                       //新增了一個元素子節點,觸發回撥函式.

    // 隨後,你還可以停止觀察
    // observer.disconnect();


    /*
    * doc  https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
    */
執行結果
/*
appending
childList
attributes
setTimeout
*/

MutationObserver

相關文章