Skip to main content

响应式

vue.$set

vue 里面的 $set

  1. definePorperty 只能遍历对象属性进行响应式,新增的属性不是响应式的
  2. $set 对于数组来说会直接使用 splice 进行增删改操作
  3. $set 对于对象会重新判断这个新增的属性是不是响应式的,对象是不是响应式的,再调用 defineReactive 方法

响应式原理

vue 双向数据绑定原理?

vue 是通过基于发布订阅模式(观察者)的数据劫持来实现双向数据绑定的

  • Observer 观察者函数:监听所有数据的变化,当数据变动时获取最新的值并通知给订阅者(数据劫持)
  • Watcher 订阅者函数:当接受到观察者的通知和提供的数据后同步更新视图
  • Compile 解析器函数:解析 DOM 元素上的 v-model 指令和 {{}} 语法

数据劫持+观察者模式

1.初始化响应对象

给 data 中对象的每个 key 创建 dep(放在__ob__里)

observe(this.$data);

defineReactive(obj, key, obj[key]);

2.编译模板并依赖收集

对每个组件,生成一个 watcher,

依赖收集:

如果用到了某个 key,创建 watcher 时会触发该 key 的 getter

进而将该 watcher 加入到 key 对应对象的 dep 里(即依赖中收录该订阅者)

Dep.target 挂载当前 watcher

3.以后触发 setter 时

会触发 Dep 上的 notify,通知所有该属性相关的 watcher

从而调用 watcher 的 执行 this.updaterFn.call()

改变视图

4.在 update 中直接将 exprssion 映射为实际属性

而在绑定响应式时,会同时将 a.b.c 以字符串形式储存起来作为 expression

该 expression 会映射回去,于是就可以在组件的 data 中找到对应的 a.b.c(而且类似组件属性上的 value,跟视图直接绑定),这时修改其值,再触发回调重新渲染

vue3 采用 proxy

1. Object.defineProperty 的缺点

  • 不能监听数组:因为数组长度不确定,如果太长性能负担太大
  • 只能监听属性,而不是整个对象(于是产生了 ref)
  • 需要遍历循环属性(暴力递归)
  • 只能监听属性变化,不能监听属性的删减

为什么不对数组使用 Object.defineProperty

对于数组,Object.defineProperty 也可以监听变化

但是数组长度不确定,如果用 defineProperty 实现响应式,每次长度改变都要重新进行响应式监听,太长性能负担过大

根据尤大所说,性能负担和收益不成正比

2. proxy 的好处

  • 可以监听数组
  • 监听整个对象不是属性
  • 13 种拦截方法,强大很多
  • 返回新对象而不是直接修改原对象,更符合 immutable;

3. proxy 的缺点

  • 兼容性不好,而且无法用 polyfill 磨平

Vue 无法检测 property 的添加或移除

因为 Vue 只会在初始化实例时对 property 执行 getter/setter 转化(遍历属性)

push, pop, shift, unshift, splice, sort, reverse

对于返回新数组

filter concat slice