ref
接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。
import { ref } from 'vue'
const name = ref('小张')
const change = () => {
// 修改name 页面会响应式修改
name.value = '小张change'
}
当 ref 在模板中作为顶层属性被访问时,它们会被自动“解包”,所以不需要使用 .value
<template>
<button @click="increment"> {{ count }} <!-- 无需 .value --> </button>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>
shallowRef
用来创建一个浅层响应式对象;如果是复杂数据类型,只有根级别的属性是响应式的
import { ref, isRef, shallowRef, triggerRef, customRef } from 'vue'
// isRef 判断是否为ref对象
// ref 深层次 shallowRef 浅层次的响应
const man1 = ref({ name: '小张' })
const man2 = shallowRef({ name: '小李' })
const change = () => {
// 修改man1 页面会响应式修改
man1.value.name = '小张change'
// 页面不会修改
man2.value.name = '小李change'
// 页面会响应式修改
man2.value = { name: '小李change' }
// 同时修改ref 和 shallowRef 页面会同步响应
man1.value.name = '小张change'
man2.value.name = '小李change'
// 注意: ref 和 shallowRef 不能一起写 会影响shallowRef 造成视图更新
}
ref 与 shallowRef 源码解析
源码路径: /packages/reactivity/src/ref.ts
// ref
// 通过函数重载,支持传入多种类型
export function ref<T extends Ref>(value: T): T
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
return createRef(value, false) // ref 传入false代表需要通过reactive做深层次的响应
}
// shallowRef
// 通过函数重载,支持传入多种类型
export function shallowRef<T extends object>(
value: T
): T extends Ref ? T : ShallowRef<T>
export function shallowRef<T>(value: T): ShallowRef<T>
export function shallowRef<T = any>(): ShallowRef<T | undefined>
export function shallowRef(value?: unknown) {
return createRef(value, true) // shallowRef 传入true 代表只需要做浅层响应 只到.value
}
createRef
function ## createRef(rawValue: unknown, shallow: boolean) {
// 如果以及是一个ref对象,直接返回
if (isRef(rawValue)) {
return rawValue
}
// 不是的话就通过 RefImpl 创建一个ref对象
return new RefImpl(rawValue, shallow)
}
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) {
// 通过 __v_isShallow 判断是否通过shallowRef调用, 如果是的话直接返回
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value) // 这里使用toReactive去对引用类型数据创建响应式对象
}
get value() {
// 收集依赖
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)
// 触发依赖更新
triggerRefValue(this, newVal)
}
}
}
isRef
用来判断是不是一个ref对象
import { ref, Ref, isRef } from 'vue'
const name: Ref<string | number> = ref('小张')
const notRef: number = 123
const checkRef = () => {
name.value = "change name"
console.log(isRef(name)); // true
console.log(isRef(notRef)); // false
}
triggerRef
强制更新页面DOM
// 调用此方法进行强制更新, ref底层已调用过
triggerRef()
customRef
创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
customRef 是个工厂函数要求我们返回一个对象, 这个工厂函数接受 track 和 trigger 两个函数作为参数,并返回一个带有 get 和 set 方法的对象。
一般来说,track() 应该在 get() 方法中调用,而 trigger() 应该在 set() 中调用。然而事实上,你对何时调用、是否应该调用他们有完全的控制权。
// 自定义hook防抖函数
function useMyRef<T>(value: T) {
let timer:any
return customRef((track, trigger) => {
return {
// 返回数据
get() {
// 追踪数据
track()
return value
},
// 设置数据
set(newVal) {
clearTimeout(timer)
timer = setTimeout(() => {
console.log('触发了');
value = newVal
timer = null
trigger()
}, 500)
}
}
})
}
const obj = MyRef<string>('校长')
const handleChange = () => {
obj.value = '我是小张'
}
通过ref获取dom属性
<template>
<button @click="handleChange">获取dom</button>
<div ref="dom">我是dom</div>
</template>
import { ref } from 'vue'
const dom = ref<HTMLElement>()
const handleChange = () => {
console.log(dom.value?.innerHTML)
}
unref
如果参数是 ref,则返回内部值,否则返回参数本身。
import { ref } from 'vue'
const dom = ref<Number>(1)
unref(val)
// 相当于下面的语法糖
val = isRef(val) ? val.value : val