reactive()
的种种限制归根结底是因为 JavaScript 没有可以作用于所有值类型的 “引用” 机制。为此,Vue 提供了一个 ref() 方法来允许我们创建可以使用任何值类型的响应式 ref。通过.value来获取响应式对象
本文vue的版本是:3.2.45
下面是ref可以定义任何类型的例子。
import { ref } from 'vue'
const count = ref(0)
const objectRef = ref({ count: 0 })
// 这是响应式的替换
objectRef.value = { count: 1 }
来看看他的内部代码实现:位置在packages/reactivity/ref.ts中
export function ref(value?: unknown) {
return createRef(value, false)
}
// 第一层为响应式数据
export function shallowRef(value?: unknown) {
return createRef(value, true)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
createRef接收两个参数,第一个是目标值,第二个是否是浅引用。如果是响应式则直接返回。否则创建 RefImpl实例。再看看RefImpl的实现
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) {
// ref 中__v_isShallow为false shallowRef为true
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}
get value() {
// 实现依赖收集的函数,内部调用trackEffects函数
// 这里的this指向实例
trackRefValue(this)
return this._value
}
set value(newVal) {
const useDirectValue =
this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
newVal = useDirectValue ? newVal : toRaw(newVal)
// 判断值是否相等,内部类似严格相等 内部取反值
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = useDirectValue ? newVal : toReactive(newVal)
// 进行依赖触发,内部调用triggerEffects函数
triggerRefValue(this, newVal)
}
}
}
这里不再看依赖收集和依赖触发的过程了,上篇文章中有具体代码。 源码中有hasChanged方法用来判断值是否相等。
export const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)
这里讲解Object.is():Object.is()
不等价于 === 运算符。Object.is()
和 ===
之间的唯一区别在于它们处理带符号的 0 和 NaN
值的时候。
Object.is(+0,-0) // false
Object.is(NaN,NaN) // true
toRaw和toReactive的代码实现
export function toRaw<T>(observed: T): T {
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
return raw ? toRaw(raw) : observed
}
// 如果目标值为对象类型,就会调用reactive创建响应式
export const toReactive = <T extends unknown>(value: T): T =>
isObject(value) ? reactive(value) : value