isRef & unRef
isRef和unRef的实现都非常简单,就写在一起啦!
测试用例
it('isRef', () => {
// 生命两个响应式数据 a 和 user
const a = ref(1)
const user = reactive({ a: 1 })
// a 是ref
expect(isRef(a)).toBe(true)
// 1不是
expect(isRef(1)).toBe(false)
// user不是ref
expect(isRef(user)).toBe(false)
})
it('unRef', () => {
const a = ref(1)
// 如果传入的值是ref,返回原始值
expect(unRef(a)).toBe(1)
// 如果是原始值,返回本身
expect(unRef(1)).toBe(1)
})
描述
isRef:用来判断传入的值是不是一个ref
unRef:如果传入的值是一个ref,返回它的value属性,否则返回自身
实现
/*
* isRef和unRef的实现主要在于RefImpl类中新增的__v_isRef属性
* 用它来判断是否是ref,或进行操作
*/
// 更新RefImpl
class RefImpl {
... // 省略
// 增加一个属性写死的 __v_isRef属性,通过ref实例的 __v_isRef 属性判断它是不是一个ref
public __v_isRef = true
... // 省略
}
// 判断传入的值是不是ref
export function isRef (ref) {
// 如果传入的是一个原始数据类型,那么将返回undefined,所以通过取反两次将其转为布尔值
return !!ref.__v_isRef
}
// 传入一个ref数据,返回它的原始值
export function unRef (ref) {
// 如果是ref,返回它的value属性,否则直接返回就可以
return isRef(ref) ? ref.value : ref
}
proxyRef
测试用例
it('proxyRef', () => {
// 创建一个对象
const user = {
// age属性是一个ref
age: ref(10),
// name属性是基本类型数据
name: 'lufei'
}
// 创建user的代理
const proxyUser: any = proxyRef(user)
// user.age.value === 10
expect(user.age.value).toBe(10)
// proxyUser.age === 10,省去了.value的操作
expect(proxyUser.age).toBe(10)
// proxyUser.name === 'lufei',与普通对象无异
expect(proxyUser.name).toBe('lufei')
// ---------上面测试的实际上是proxyUser读取值时的操作----------
// 将要更新的是一个原始值
proxyUser.age = 20
// 更新后 proxyUser.age === 20,同读取一样,省去了.value的操作
expect(proxyUser.age).toBe(20)
// proxyUser更新后,相对于proxyUser的原始对象(也就是user)也会更新
// user.age.value === 20
expect(user.age.value).toBe(20)
// 区别在于将要更新的值是一个ref
proxyUser.age = ref(10)
// 下面测试与上面同理
expect(proxyUser.age).toBe(10)
expect(user.age.value).toBe(10)
// ---------上面测试的则是proxyUser设置值时的操作----------
})
描述
代理ref,比如在vue模板(template)中使用ref时,不需要.value,直接使用就可以。
更新它的值时同时更新它的原始对象
因为proxyRef是用来代理ref的,且核心逻辑在get和set中,所以需要返回一个Proxy
根据测试用例不难发现:
get:读取proxyRef时,需要通过 Reflect.get(target, key) 来获取值,但获取到的值是一个ref,所以通过unRef获取到它的原始值
set:set共分为四种情况
当前的值是ref 且 将要更新的值不是ref的时候,需要给当前值(target[value])的value属性赋值
否则直接给这个当前值取赋值就可以(这个重点在于当前值(target[key])是不是ref
两个都不是ref的情况下,简单赋值就可以:target[key] = value
在当前值不是一个ref,将要更新的值是ref,也是直接赋值:target[key] = value
在当前值(target[key])是一个ref,将要更新的值也是ref的情况下(两个值都是引用类型),需要修改的就是target[key]的指针,要做的操作也是:target[key] = value
实现
function proxyRef (objectWithRefs) {
return new Proxy(objectWithRefs, {
get (target, key) {
return unRef(Reflect.get(target, key))
},
/*
* set的时候判断它是不是ref类型,如果是ref类型,那么需要修改它的value属性
* 否则
* */
set (target, key, value) {
/*
* 当前的值是ref 且 将要更新的值不是ref的时候,需要给当前值(target[value])的value属性赋值
* 否则直接给这个当前值取赋值就可以(这个重点在于当前值(target[key])是不是ref,
* 如果不是那么做简单的赋值操作就可以)
*
* 1.在当前值不是一个ref,将要更新的值是ref,也是直接赋值:target[key] = value
* 2.两个都不是ref的情况下,简单赋值就可以:target[key] = value
* 在当前值(target[key])是一个ref,将要更新的值也是ref的情况下(两个值都是引用类型),
* 需要修改的就是target[key]的指针,要做的操作也是:target[key] = value
* */
if (isRef(target[key]) && !isRef(value)) {
return target[key].value = value
} else {
return Reflect.set(target, key, value)
}
}
})
}