137 lines
4.0 KiB
Markdown
137 lines
4.0 KiB
Markdown
# 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 方式
|
||
|
||
**对应源码**
|
||
|
||
```js
|
||
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
|
||
```
|
||
|
||
```js
|
||
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 在响应式的实现方面有这么一些方面的改变:
|
||
>
|
||
> 1. 数据拦截从 Object.defineProperty 改为了 Proxy + Object.defineProperty 的拦截方式,其中
|
||
> - ref:使用 ObjectdefineProperty + Proxy 方式
|
||
> - reactive:使用 Proxy 方式
|
||
> 2. 创建响应式数据在语法层面有了变化:
|
||
> - Vue2: 通过 data 来创建响应式数据
|
||
> - Vue3: 通过 ref、reactvie 等方法来创建响应式数据
|
||
> 3. 依赖收集上面的变化
|
||
> - Vue2:Watcher + Dep
|
||
> - Vue3:WeakMap + Map + Set
|
||
> - 这种实现方式可以实现更细粒度的依赖追踪和更新控制
|
||
|
||
---
|
||
|
||
-EOF- |