Skip to content

0深入响应式原理

深入响应式原理

追踪变化

一个普通的JS对象,如果是放到Vue的data() {}里,那么Vue会对此对象的属性进行遍历。 然后将这些属性用 Object.defineProperty把这些属性全部转为getter/setter

变化检测

Vue不能检测到对象属性的添加或删除 Vue在初始化实例时才对属性执行getter/setter转化过程,所以属性必须在data对象上存在才能让Vue转化它。

将响应属性添加到对象上:

js
// 大实例上
Vue.set(vmObject, key, value);
// 组件里
this.$set(this.vmObject, key, value);
// 有时想向已有对象上添加属性,但直接添加不会触发刷新。
// 我们可以创建一个新的对象,让它包含原对象的属性和新的属性值
this.someObject = Object.assign({}, this.someObject, {a: 1, b: '2'})

声明响应式属性

由于Vue不允许动态添加根式响应式属性,所以你必须在实例化实例前声明根级响应式属性,哪怕是一个空值

js
var vm = new Vue({
  data: {
    message: ''
  },
  template: '<div>{{message}}</div>'
})
vm.message = 'hello';

异步更新队列

Vue异步执行DOM更新。只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个watcher被多次触发,只会一次推入到队列中。这种缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。 然后,在下一个事件循环tick中,Vue刷新队列并执行实际(已去重)工作。 Vue在内部尝试对异步队列使用原生的Promise.thenMutationObserve,如果执行环境不支持,采用setTimeout(fn, 0)替代。

虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。例如:

js
// <div id="example">{{message}}</div>

var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new'; // 更新数据
vm.$el.textContent === 'new' // false
Vue.nextTick(function() {
  vm.$el.textContent === 'new' // true
})

在组件内使用 vm.$nextTick() 实例特别方便,因为它不需要全局Vue,并且回调函数中的this将自动绑定到Vue实例上

js
Vue.component('example', {
  template: '<span>{{message}}</span>',
  data() {
    return {
      message: '没有更新'
    }
  },
  methods: {
    updateMessage() {
      this.message = '更新完成'
      console.log(this.$el.textContent) // 没有更新
      this.$nextTick(() => {
        console.log(this.$el.textContent) // 更新完成
      })
    }
  }
})

共 20 个模块,1301 篇 Markdown 文档。