ref与ractive的区别
864字约3分钟
2024-07-27
介绍
我们都知道ref
和reactive
是Vue 3中用于创建响应式数据的两个API,但是其内部运行的原理 和区别是什么呢?
- reative.ts
- ref.ts
reactive
源码
/**
* 目标对象
* @param target
* 是否只读
* @param isReadonly
* 基本类型的 handlers
* @param baseHandlers
* 主要针对(set、map、weakSet、weakMap)的 handlers
* @param collectionHandlers
* WeakMap数据结构
* @param proxyMap
* @returns
*/
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
// typeof 不是 object 类型的,在开发模式抛出警告,生产环境直接返回目标对象
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
// 已经是响应式的就直接返回(取ReactiveFlags.RAW 属性会返回true,因为进行reactive的过程中会用weakMap进行保存,
// 通过target能判断出是否有ReactiveFlags.RAW属性)
// 对reactive对象进行readonly()
if (target[ReactiveFlags.RAW] &&!(isReadonly && target[ReactiveFlags.IS_REACTIVE])) {
return target
}
// 对target已经是Proxy的,则直接从WeakMap数据结构中取出这个Proxy对象
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// 只对targetTypeMap类型白名单中的类型进行响应式处理
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// proxy 代理 target类型
// (set、map、weakSet、weakMap) collectionHandlers
// (Object、Array) baseHandlers
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
ref
源码
// 创建ref对象
function createRef(rawValue: unknown, shallow: boolean) {
// 判断是不是响应式对象,是的话直接返回
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
class RefImpl<T> {
// 响应式值
private _value: T
// 原始值
private _rawValue: T
public dep?: Dep = undefined
//只读属性 表示对象是ref个响应式对象
public readonly __v_isRef = true
constructor(
value: T,
// 是否深度监听 默认深度监听
public readonly __v_isShallow: boolean,
) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
// 判断是否当前对象深度监听、新值对象是否浅层监听、可读、
const useDirectValue =this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
// toRaw返回的是原始值
newVal = useDirectValue ? newVal : toRaw(newVal)
// 判断新旧值是否相等 内部使用Object.is方法判断
if (hasChanged(newVal, this._rawValue)) {
const oldVal = this._rawValue
this._rawValue = newVal
//新值是否满足 是的化直接返回,不是则调用reactive处理返回
this._value = useDirectValue ? newVal : toReactive(newVal)
// 触发依赖,派发更新
triggerRefValue(this, DirtyLevels.Dirty, newVal, oldVal)
}
}
}
//
// 将对象转换为响应式 如果不是对象返回值本身
const toReactive = <T extends unknown>(value: T): T => isObject(value) ? reactive(value) : value
// 判断根据对象的__v_isRef属性判断是不是ref对象
function isRef(r: any): r is Ref {
return Boolean(r && r.__v_isRef === true)
}
区别介绍
reactive
是针对对象进行响应式处理,ref
是针对基本数据类型进行响应式处理reactive
返回的是代理对象,ref
返回的是RefImpl
对象reactive
返回的对象是深度的响应式对象,ref
返回的对象是浅层响应式对象
个人总结
其实根据源码可以知道ref其实本质也可以声明复杂数据类型,虽然内部调用走的还是reactive 申明。
思路:ref设置值
- 判断值是不是已经是响应式的
- 返回原始值判断是否变动
- 变动的话判断是不是对象
- 是的话调用reactive处理,不是的话直接返回新值
- 触发依赖(后续依赖函数去源码里看吧!!)