基础数据类型转响应式 ---> ref
ref 的用法比较简单,只需要将基本类型的数据传入,通过返回值的 value 属性获取响应式的值即可。
const a = ref(0)
console.log(a.value) // 0
其实他也可以接受复杂数据类型作为参数,但是其内部依然是调用 reactive api 进行的响应式处理,这个过程对用户来说是隐藏的。所以,为了使代码更清晰,你应该使用 ref 去处理基本类型的影响式数据,而使用 reactive 去处理复杂类型的数据(注意,这里指的不是所有的负责类型的数据)
ref 的相关 api
ref
ref 是最基本的 api,将你传入的数据包装成响应式数据
isRef
isRef 用于判断传入的参数是不是 ref 包装的响应式数据
const a = ref(1)
const b = 2
cosnole.log(isRef(a)) // true
console.log(isRef(b)) // false
其实内部是判断数据对象上是否包含__v_isRef 属性并且其值为 true。
shallowRef
shallowRef 提供了对各种数据类型的包装,他的处理很简单,不论你传入什么值,直接将其挂载在返回值得 value 属性上。使用此 api 时,直接对 value 赋值才会产生响应式变化,否则是不会触发响应式的更新,详情看下面的 triggerRef
const a = shallowRef({a:1})
console.log(isRef(a)) // true
console.log(a.value) // {a:1}
triggerRef
triggerRef 用来主动地触发依赖于某个响应式数据的所有 effect,这个 api 调用场景可能不是很多。
const sref = shallowRef({ a: 1 })
let dummy
effect(() => {
dummy = sref.value.a
})
console.log(dummy) //1
sref.value.a = 2
console.log(dummy) //1
// 手动调用 triggerRef
triggerRef(sref)
console.log(dummy) //2
unref
unref 返回使用 ref 系列 api 处理的响应式对象的源值,其实就是返回其valiue 属性对应的值。源码如下
function unref(ref){
return isRef(ref) ? ref.value : ref
}
toRef
toRef 将对象的某个键变为响应式对象,这在 reactive 处理的对象获取属性时某个属性响应式丢失时很有用。
const a = reactive({
x: 1
})
const x = toRef(a, 'x')
console.log(isRef(x)) // true
console.log(x.value) // 1
// 改变 reactive 数据
a.x = 2
console.log(x.value) // 2
// 改变 ref 数据
x.value = 3
console.log(a.x) //3
let dummyX
effect(() => {
dummyX = x.value
})
console.log((dummyX === x.value)) // true
// reactive 改变,也会是 toRef 的数据发生改变
a.x = 4
console.log(dummyX) // 4
toRefs
toRefs 是 toRef 的批量版本。会将传入对象的每个属性处理为 ref 的值。
customRef
customRef api 提供了用户自定义 ref 的操作,下面是他的源码
export function customRef(factory: CustomRefFactory) {
const { get, set } = factory(
() => track(r, TrackOpTypes.GET, 'value'),
() => trigger(r, TriggerOpTypes.SET, 'value')
)
const r = {
__v_isRef: true,
get value() {
return get()
},
set value(v) {
set(v)
}
}
return r
}
可以看到,出入的回调函数中需要返回自定义的 get 和 set 函数,就完成了整个自定义 ref 的过程。 vue3 会给你注入 track(在 get 中调用) 和 tirgger (在 set 中调用) 函数,以方便进行数据追踪。
ref 和 shallowRef 的源码实现
function createRef(rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue
}
let value = shallow ? rawValue : convert(rawValue)
const r = {
__v_isRef: true,
get value() {
track(r, TrackOpTypes.GET, 'value')
return value
},
set value(newVal) {
if (hasChanged(toRaw(newVal), rawValue)) {
rawValue = newVal
value = shallow ? newVal : convert(newVal)
trigger(
r,
TriggerOpTypes.SET,
'value',
__DEV__ ? { newValue: newVal } : void 0
)
}
}
}
return r
}
整个代码流程很清晰,其中的 convert 就是 ref api 处理复杂类型的关键,代码如下
const convert = val => isObject(val) ? reactive(val) : val
小结
以上是个人阅读 vue3 ref 源码的记录。