常用响应式API与原理分析-ref

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,然后分发依赖。
refreactive相关的疑问
  • 基础类型数据变为响应式数据使用refAPI,对象或数组变为响应式对象用reactiveAPI?
    • ref也可以将对象数组变为响应式数据,因为其内部实现机制也是基于reactive
  • 既然ref包含了reactive功能,为何不能只使用refAPI?
    • ref一大特点是提供了set方法,可以将整个原始数据value完全替换,类似于let,而reactive只能对原始数据value的属性进行修改,类似于const的限制
    • ref类似于letreactive类似于const,两者的使用场景不一样。

shallowRef

使用场景:复杂数据类型只需要监测对象的替换,不需要监测对象的属性修改;原始数据类型shallowRefref的效果没有区别。

创建一个跟踪自身.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持有返回对象的getset方法,用于执行赋值和取值

实现原理:

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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值