https://blog.csdn.net/lcl130/article/details/120131085?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168016836716800192258255%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=168016836716800192258255&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-120131085-null-null.142v78insert_down38,201v4add_ask,239v2insert_chatgpt&utm_term=ObjectRefImpl&spm=1018.2226.3001.4187
-
reactive
对传入的类型是有限制的,必须是对象或数组,对一些基础类型(例如string
/number
/boolean
等)不支持。 -
Ref
是一个接口
,最主要的是有一个value
属性可以获取值和赋值。export interface Ref<T = any>{ value: T _shallow?: boolean }
ref
使用场景:将数据变为响应式数据。
实现原理:
-
createRef
传入的参数2如果已经是ref
对象,直接返回,如果不是就利用RefImpl
进行封装export function ref(value?: unknown) { rerturn createRef(value) } function createRef(rawValue:unknown, shallow=false){ // 如果已经是ref对象,直接返回该对象 if(isRef(rawValue)){ return rawValue } // RefImpl对rawValue进行封装 return new RefImpl(rawValue, shallow) }
-
RefImpl
有两个私有变量_value
和_rawValue
,_rawValue
是原始值,_value
是操作的值- 如果
value
值是原始数据,_value
和_rawValue
都等于value
; - 如果
value
值是数组或者对象,_value
被转换成了reactive
对象,_rawValue
就是响应式对象的原始数据; get
函数先收集依赖,然后返回_value
是操作的值;set
函数先比对原始值有没有变化,如果变化了就设置_value
和_rawValue
,然后分发依赖。
- 如果
ref
和reactive
相关的疑问
- 基础类型数据变为响应式数据使用
ref
API,对象或数组变为响应式对象用reactive
API?ref
也可以将对象或数组变为响应式数据,因为其内部实现机制也是基于reactive
- 既然
ref
包含了reactive
功能,为何不能只使用ref
API?ref
一大特点是提供了set
方法,可以将整个原始数据value
完全替换,类似于let
,而reactive
只能对原始数据value
的属性进行修改,类似于const
的限制- 即
ref
类似于let
,reactive
类似于const
,两者的使用场景不一样。
shallowRef
使用场景:复杂数据类型只需要监测对象的替换,不需要监测对象的属性修改;原始数据类型shallowRef
和ref
的效果没有区别。
创建一个跟踪自身.value
变化的ref,但是其值不会变成响应式。
const p = {name: "aaaaa"};
const person = shallowRef(p);
person.value.name = "haha"; // 不会检测到数据变化
person.value = {name: "yoyo"}; // 会监测到数据替换
reactive
不存在替换对象的情况,所有shallowReactive
是能监测到外部属性的变化,不能监测到内部属性的变化。
实现原理:
triggerRef
作用:强制更新页面DOM
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { Ref, shallowRef,triggerRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value.name = '大满'
triggerRef(message)
}
</script>
<style>
</style>
isRef
使用场景:判断一个对象是否是ref
对象
实现原理:isRef
就是判断__v_isRef
是否为true
export function isRef(r:any):r is Ref{
// 判断对象的__v_isRef是否是true
return Boolean(r && r.__v_isRef === true)
}
class RefImpl<T>{
// __v_isRef设置为true
public readonly __v_isRef = true;
}
unref
使用场景:获取ref
对象的_value
值,有可能是reactive
对象(因为不是获取_rawValue
的值)
实现原理:
export function unref<T>(ref: T | Ref<T>):T{
// 如果是ref对象就调用 get 方法获取 _value 的值,否则返回对象本身
return isRef(ref) ? (ref.value as any) : ref
}
toRef(obj, key)
使用场景:将reactive
响应式对象的某个属性创建为一个ref
对象,方便赋值与取值
const zhangshanfeng = reactive({
name: '张三丰',
age: 100,
child: {
name: '张翠山',
age: 40,
child: {
name: '张无忌',
age: 20
}
}
})
const wuji = toRef(zhangshanfeng.child, 'child'); // 获得张无忌的ref对象
wuji.value.age += 10; // 修改张无忌的年龄
- 可以用于处理网络请求的返回值的处理,在某些请求中只有一部分数据需要展示
toRefs
使用场景:将reactive
响应式对象的,每个属性创建一个ref
对象。方便赋值与取值
实现原理:就是对每个属性分别执行toRef
调用
customRef
自定义一个ref
对象,实现自己的功能。customRef
是个工厂函数要求返回一个对象,customRef
的参数track
是()=>trackRefValue(this)
,trigger
是()=>triggerRefValue(this)
,可以收集依赖和分发依赖,customRef
持有返回对象的get
和set
方法,用于执行赋值和取值
实现原理:
class CustomRefImpl<T>{
public dep?: Dep = undefined
// 持有传入的get和set方法
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
private readonly _set: ReturnType<CustomRefTactory<T>>['set']
public readonly __v_isRef = true
constructor(factory: CustomRefTactory<T>){
const { get, set } = factory(
// 将收集依赖和分发依赖的方法作为参数传递出去
() => trackRefValue(this),
() => triggerRefValue(this)
)
this._get = get
this._set = set
}
// get是调用传入的get方法
get value(){
return this._get()
}
// set是调用传入的set方法
set value(newVal){
this._set(newVal)
}
}
使用场景:可以用来实现防抖功能
<script>
import { ref, customRef } from "vue";
function debounceRef<T = any>(value:T){
let timer:any;
return customRef((track, trigger) => {
return {
get(){
track();
return value
},
set(newVal){
clearTimeout(timer);
timer = setTimeout(() => {
console.log('触发');
value = newVal;
trigger()
}, 500)
}
}
})
}
const name = debounceRef<string>('zxx');
const change = () => {
name.value = "zzxxxx";
}
</script>