# 响应式原理

概述

响应式就是在修改数据对象的时候会触发视图更新

# 追踪变化

原理

Vue 会遍历 data 选项中所有 property,然后使用 Object.defineProperty 设置所有 property 的自定义 getter/setter,触发 setter 会通知 watcher 使得相关联的组件重新渲染

追踪变化

# 检测变化的特殊情况

# 对象

无法检测对象属性的添加或刪除,所以

  1. 在初始化的时候声明:
var vm = new Vue({
  data: {
    a: 1,
  },
});

// `vm.a` 是响应式的

vm.b = 2;
// `vm.b` 是非响应式的
1
2
3
4
5
6
7
8
9
10
  1. 添加单个属性,使用 $set 添加响应式属性:
this.$set(this.someObject, "b", 2);
1
  1. 添加多个属性,和原对象混合创建新的对象:
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 });
1
2

# 数组

Vue 不能检测以下数组的变动:

  • 利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
    • 使用 $set 修改:Vue.set(vm.items, indexOfItem, newValue)
    • 使用 splicevm.items.splice(indexOfItem, 1, newValue)
  • 修改数组的长度时,例如:vm.items.length = newLength
    • 使用 splice: vm.items.splice(newLength)

# 异步更新队列

Vue 在更新 DOM 时是异步执行的:

  1. 监听到数据变化
  2. 开启队列
  3. 缓存同一事件循环中发生的所有数据变更(同一个 watcher 多次触发只会被加入到队列一次)
  4. 下一次事件循环 tick 中刷新队列执行去重后的任务

Vue 在内部对异步队列尝试使用原生的 Promise.thenMutationObserversetImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替

如果想要基于更新后的 DOM 状态来做点什么,可以在数据变化之后立即使用 Vue.nextTick(callback),这样回调函数将在 DOM 更新完成后被调用

(2.6 之前的版本) Vue.nextTick 一般情况下是微任务,在处理事件绑定的时候是宏任务(事件冒泡可能导致顺序错误)

最新的冒泡做了特殊处理,所有都是微任务

# proxydefineproperty 相比优势

优势:

  • 可以直接监听对象而非属性
  • 可以直接监听数组变化
  • 有 13 种拦截方法
  • 返回一个新对象,对新对象操作,而 defineproperty 只能遍历对象属性直接修改
  • 作为新标准将会收到浏览器厂商重点持续的性能优化

劣势:

  • defineproperty 兼容性好,ie9,无法通过 polyfill 实现