前言
上篇介绍了reactive,这篇介绍一下ref相关的api
class
上篇说reactive和Proxy息息相关,而ref和reactive及class息息相关,接下来看源码
ref
源码实现:
export function ref(value?: unknown) {
return createRef(value, false)
}
function createRef(rawValue: unknown, shallow: boolean) {
if (isRef(rawValue)) {
return rawValue
}
return new RefImpl(rawValue, shallow)
}
vue通过createRef返回RefImpl,RefImpl是一个class,看一下vue是如何实现RefImpl的
class RefImpl<T> {
private _value: T
private _rawValue: T
public dep?: Dep = undefined
public readonly __v_isRef = true
constructor(value: T, public readonly _shallow: boolean) {
this._rawValue = _shallow ? value : toRaw(value)
this._value = _shallow ? value : convert(value)
}
get value() {
trackRefValue(this)
return this._value
}
set value(newVal) {
newVal = this._shallow ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : convert(newVal)
triggerRefValue(this, newVal)
}
}
}
通过代码可以看到get获取值时是返回this._value,默认情况下通过convert获取真正的数据,那么我们一起看一下convert是如何实现的
const convert = <T extends unknown>(val: T): T => isObject(val) ? reactive(val) : val
此处可以看出一些端倪了,当ref入参为对象时会走reactive的proxy的代理,否则走RefImpl的set和get方法,这也就解释了开篇说的ref和reactive及class息息相关,看一下简化后的代码
// 若为对象走reactive
const convert = val => isObject(val) ? reactive(val) : val
class RefImpl {
constructor(value, shallow) {
this.__v_isRef = true
this._rawValue = shallow ? value : toRaw(value)
this._value = shallow ? value : convert(value)
this._shallow = shallow
}
get value() {
return this._value
}
set value(newValue) {
newValue = this._shallow ? newValue : toRaw(newValue)
if(hasChanged(newValue, this._rawValue)) {
this._rawValue = newValue;
this._value = this._shallow ? newValue : convert(newValue)
}
}
}
function createRef(value, shallow) {
if(isRef(value)) return value
return new RefImpl(value, shallow);
}
function ref(value) {
return createRef(value, false)
}
isRef
RefImpl中有一个只读的__v_isRef属性,这也是判断是否为ref的依据
function isRef(r) {
return r && r.__v_isRef === true
}
unref
官网解释:如果参数是一个 ref,则返回内部值,否则返回参数本身
function unref(ref) {
return isRef(ref) ? ref.value : ref
}
toRef
toRef的实现同样是通过自定义class的set和get方法对value属性进行操作,从而达到操控源对象
class ObjectRefImpl {
constructor(_object, _key) {
this.__v_isRef = true
this._object = _object
this._key = _key
}
get value() {
// 获取reactive值
return this._object[this._key]
}
set value(newVal) {
// 设置reactive值
this._object[this._key] = newVal
}
}
function toRef(target, key) {
const value = target[key]
return isRef(value) ? value : new ObjectRefImpl(target, key)
}
toRefs
toRefs是借助toRef对响应式对象的操作
function toRefs(object) {
if (!isProxy(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`)
}
const ret = isArray(object) ? new Array(object.length) : {}
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret
}
shallowRef
shallowRef会跟着ref的改变,但不会让.value变为响应式的,入参时将shallow设置为true,此时ref的入参为对象时将不会在走convert
function shallowRef(value) {
return createRef(value, true)
}
customRef
customRef接收一个工厂函数,工厂函数需返回自定义的get和set方法,当获取和赋值的时候会分别调用自定义的get和set方法,此处并未涉及数据收集过程,仅是单纯的逻辑实现
class CustomRefImpl {
constructor(factory) {
const { get, set } = factory()
this._get = get
this._set = set
}
get value() {
return this._get()
}
set value(newValue) {
this._set(newValue)
}
}
function customRef(factory) {
return new CustomRefImpl(factory)
}
结语
这次看源码有了写意外收获