- 首先我们说vue的核心设计思想就是响应式,本质是当数据发生变化后会自动执行某个函数映射到组件的实现就是,当数据变化后,会自动触发组件中的重新渲染
- 响应式是vue组件化更新渲染的一个核心机制
Object.defineProperty API 的缺点:
- 不能监听对象属性增加和删除
- 初始化阶段递归执行Object.defineProperty带来的性能负担
- 如果说我们在created生命周期中定义了一个
this.msg=xxx
这个msg变量并不是响应式的,只有我们在data中定义的数据才是响应式的变量—这也算是一个小的性能优化(我们如果上下文要共享使用一个变量,不需要他是响应式的,就可以使用这种方法)vue3通过 proxy API重写了响应式部分,并独立维护及发布整个reactivity库
- reactive API 可以把一个对象数据变成响应式的。这样我们就能明确哪些是响应式的
reactive通过createReactiveObject主要做了以下几件事
- 函数首先判断target是不是数组或者对象类型,如果不是直接返回,原始数据target必须是对象或者数组
- 如果对一个已经是响应式的对象再次执行reactive,应该返回这个响应式对象
- reactive函数通过target.__v_raw属性来判断target是否已经是一个响应式对象,如果是的话则直接返回响应式对象
- 如果对一个原始数据多次执行reactive,那么会返回相同的响应式对象。
- 通过ProxyApi劫持target对象,把它变成响应式对象
- 最后一件事,就是给原始数据打上标识
响应式的实现方式就是劫持数据,vue3的reactive APi就是通过Proxy劫持数据由于Proxy劫持的是整个对象,所以检测到任何对对象的修改,弥补了Object.definePropertyAPi的缺陷
劫持对observed对象的一些操作
- 访问对象属性会触发get函数
- 设置对象属性会触发set函数
- 删除对象属性会触发deleteProperty函数
- in操作符 会触发has函数
- Object.getOwnPropertyNames触发ownKeys函数
无论触发那个操作都会做依赖收集和派发通知中的一个
依赖收集:get函数
待补充,就是在访问属性的时候,通过track函数进行依赖收集
源码
这个是reactive的入口函数
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 首先判断它是不是只读的如果是只读的就直接返回
if (isReadonly(target)) {
return target
}
// 如果不是就利用createReactiveObject函数进行创建
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
createReactiveObject函数
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
// 首先判断是不是对象 是对象不执行,不是对象才会执行
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
// 如果对象或者数组已经是响应式的了 ,那么就直接返回target
// 判断在target中ReactiveFlags.RAW是否存在如果存在那就证明他是响应式的
// 然后在判断ReadOnly
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
// 目标已经有相应的proxy了
const existingProxy = proxyMap.get(target)
// 如果没有就接着往下执行,如果有就直接return 返回这个proxy对象
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
const targetType = getTargetType(target)
// 判断target的类型如果式非法的直接返回
if (targetType === TargetType.INVALID) {
return target
}
// 不是非法的,就继续对target进行代理,通过是否进行了依赖收集来进行判断用哪个函数
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
// 并把,proxy设置到弱map中,并返回proxy
proxyMap.set(target, proxy)
return proxy
}
shallowReactive也是同样通过reactive来进行的 区分的方式就是在入参时候的第三个参数和第四个参数。
set函数
其实set函数的核心部分就是trigger函数,
- trigger函数就是根据target 和key,从targetMap中找到相关的所有副作用函数遍历执行一遍