响 应式

vue.$set
vue 里面的 $set
- definePorperty 只能遍历对象属性进行响应式,新增的属性不是响应式的
- $set 对于数组来说会直接使用 splice 进行增删改操作
- $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