Vue3响应式变化
# Vue3响应式变化
面试题:说一说 Vue3 响应式相较于 Vue2 是否有改变?如果有,那么说一下具体有哪些改变?
1. 变化一
首当其冲的就是数据拦截的变化:
- Vue2: 使用 Object.defineProperty 进行拦截
- Vue3: 使用 Proxy + Object.defineProperty 进行拦截
两者的共同点
- 都可以针对对象成员拦截
- 都可以实现深度拦截
两者的差异点
- 拦截的广度
- Object.defineProperty 是针对对象特定属性的读写操作进行拦截,这意味着之后新增加/删除的属性是侦测不到的
- Proxy 则是针对一整个对象的多种操作,包括属性的读取、赋值、属性的删除、属性描述符的获取和设置、原型的查看、函数调用等行为能够进行拦截。
- 性能上的区别:在大多数场景下,Proxy 比 Object.defineProperty 效率更高,拦截方式更加灵活。
2. 变化二
创建响应式数据上面的变化:
- Vue2: 通过 data 来创建响应式数据
- Vue3: 通过 ref、reactvie 等方法来创建响应式数据
- ref:使用 Object.defineProperty + Proxy 方式
- reactive:使用 Proxy 方式
对应源码
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(
value: T,
public readonly __v_isShallow: boolean,
) {
this._rawValue = __v_isShallow ? value : toRaw(value)
// 有可能是原始值,有可能是 reactive 返回的 proxy
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
// 收集依赖 略
return this._value
}
set value(newVal) {
// 略
}
}
// 判断是否是对象,是对象就用 reactive 来处理,否则返回原始值
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>,
) {
// ...
// 创建 Proxy 代理对象
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers,
)
proxyMap.set(target, proxy)
return proxy
}
export function reactive(target: object) {
// ...
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap,
)
}
3. 变化三
依赖收集上面的变化:
Vue2:Watcher + Dep
- 每个响应式属性都有一个 Dep 实例,用于做依赖收集,内部包含了一个数组,存储依赖这个属性的所有 watcher
- 当属性值发生变化,dep 就会通知所有的 watcher 去做更新操作
Vue3:WeakMap + Map + Set
- Vue3 的依赖收集粒度更细
- WeakMap 键对应的是响应式对象,值是一个 Map,这个 Map 的键是该对象的属性,值是一个 Set,Set 里面存储了所有依赖于这个属性的 effect 函数
总结起来,Vue3相比Vue2的依赖追踪粒度更细,Vue2依赖收集收集的是具体的Watcher(组件),Vue3依赖收集收集的是对应的副作用函数。
面试题:说一说 Vue3 响应式相较于 Vue2 是否有改变?如果有,那么说一下具体有哪些改变?
参考答案:
相比较 Vue2,Vue3 在响应式的实现方面有这么一些方面的改变:
- 数据拦截从 Object.defineProperty 改为了 Proxy + Object.defineProperty 的拦截方式,其中
- ref:使用 ObjectdefineProperty + Proxy 方式
- reactive:使用 Proxy 方式
- 创建响应式数据在语法层面有了变化:
- Vue2: 通过 data 来创建响应式数据
- Vue3: 通过 ref、reactvie 等方法来创建响应式数据
- 依赖收集上面的变化
- Vue2:Watcher + Dep
- Vue3:WeakMap + Map + Set
- 这种实现方式可以实现更细粒度的依赖追踪和更新控制
-EOF-