ref的作用是提供响应式包装对象,便于利用Vue Composition API 进行函数式的组装,首先我们通过isRef函数入口,看看Vue 3.x 是如何标识ref对象的:
const isRefSymbol = Symbol()
export interface Ref<T = any> {
// This field is necessary to allow TS to differentiate a Ref from a plain
// object that happens to have a "value" field.
// However, checking a symbol on an arbitrary object is much slower than
// checking a plain property, so we use a _isRef plain property for isRef()
// check in the actual implementation.
// The reason for not just declaring _isRef in the interface is because we
// don't want this internal field to leak into userland autocompletion -
// a private symbol, on the other hand, achieves just that.
[isRefSymbol]: true // 用一个symbol来标识ref对象,但是后面又被改成了通过_isRef属性来标识
value: UnwrapRef<T> // 响应式包装对象的value属性,是解包装的值
}
// ...
export function isRef(r: any): r is Ref {
// 通过_isRef属性判断一个对象是否是ref对象
return r ? r._isRef === true : false
}
在上一篇文章Vue 3.x 响应式原理——reactive源码分析中,笔者简述了Vue 3.x 的
reactive
API 的实现原理,了解过 Vue Composition API 的同学都知道reactive
和ref
创建响应式数据的区别,本文通过讲述ref
API 的实现原理,帮助更进一步了解 Vue 3.x 的响应式原理。笔者之前也写过相关文章,也可以结合相关文章:
Ref对象
ref
的作用是提供响应式包装对象,便于利用Vue Composition API 进行函数式的组装,首先我们通过isRef
函数入口,看看Vue 3.x 是如何标识ref
对象的:看上面代码,我们可以认识到,
ref
对象总会被挂载一个叫做_isRef
的属性,所以通过_isRef
这个属性是否存在就可以帮助我们判断一个对象是否是ref
对象。此外,
ref
对象含有一个属性叫value
,value
的类型是UnwrapRef<T>
,下面看看UnwrapRef<T>
:通过上面的代码我们看到
ref
响应式包装对象的value
的类型一定是一个解包装的对象,而不能是嵌套的ref
。对于数组和对象类型,需要对其进行遍历,保证其中每项都没有嵌套ref
对象,如果有嵌套的情况,需要再进行解包装。ref
下面来看
ref
函数,ref
函数将一个普通对象转化为响应式包装对象:通过
ref
函数,我们了解到了ref
的底层就是reactive
,ref
对象具有对应的 getter 和 setter ,getter总是返回经过convert
转化后的响应式对象raw
,并触发 Vue 的依赖收集,对ref
对象赋值会调用setter
,setter
调用会通知deps,通知依赖这一状态的对象更新,并重新更新raw
,raw
被保存为新的响应式包装对象。toRefs
最后我们来看
toRefs
,toRefs
将reactive
对象转换为普通对象,其中结果对象上的每个属性都是指向原始对象中相应属性的ref
引用对象,这在组合函数返回响应式状态时非常有用,这样保证了开发者使用对象解构或拓展运算符不会丢失原有响应式对象的响应。小结
本文介绍了 Vue 3.x 的
Ref
对象的原理,ref
的作用是便于我们使用 Vue 3.x 进行组件组合时,通过函数传参不会丢失响应式对象的原始引用,其原理的核心是包装和解包装,包装时,我们保证ref
对象的value
不能嵌套ref
对象,所以使用了UnwrapRef
,同时,对于对象和数组,包装和解包装时需要对其中每项进行遍历,以保证不会出现嵌套ref
对象的情况。后面笔者会对
effect
模块进行分析,讲述 Vue 3.x 在依赖收集原理。