美文网首页
Vue3.0 常用响应式API的使用和原理分析(二)

Vue3.0 常用响应式API的使用和原理分析(二)

作者: chonglingliu | 来源:发表于2021-09-06 20:13 被阅读0次

    reactive对传入的类型是有限制的,必须是对象或者数组。对一些基础类型,例如string, numberboolean等不支持,如果要使用reactiveAPI必须将这些基础类型封装成对象,这样显然是不太科学的。因此Vue 3.0提供了refAPI

    Ref是一个接口, 它最主要的是有一个value属性可以获取值和赋值。

    export interface Ref<T = any> {
      value: T
      _shallow?: boolean
    }
    

    ref

    使用场景

    将数据变为响应式数据

    ref使用

    代码解释:

    1. 通过ref将字符串变为了一个响应式对象person
    2. 通过person.valueperson进行新值的设置,也是通过person.value获取响应式对象person的值。

    实现原理

    • createRef传入的参数如果已经是ref对象,就直接返回;如果不是就利用RefImpl进行封装。
    createRef
    • RefImpl有两个私有变量_value_rawValue, _rawValue是原始值,_value是操作的值。
      • 如果value值是原始数据,_value_rawValue都等于value
      • 如果value值是数组或者对象,_value被转换成了reactive响应式对象,_rawValue就是响应式对象的原始对象;
      • get函数先收集依赖,然后返回_value是操作的值;
      • set函数先比对原始值有没有变化,如果变化了就设置_value_rawValue,然后分发依赖。
    RefImpl

    refreactive相关的一些疑问?

    问题1:基础类型数据变为响应式对象refAPI对象或者数组变为响应式对象用reactiveAPI

    答案1:一般是这样使用的,但是ref也是可以将对象或者数组变为响应式对象的,因为其内部实现机制也是基于reactive

    问题2:既然ref包含了reactive的功能,为什么不只提供refAPI就一切都搞定了。

    答案2:ref的一个特点是提供了set方法, 可以将整个原值数据value完全替换掉,类似于let,而reactive是不能这样操作的,只能对原值数据value的属性进行修改, 类似于const的限制。

    ref 类似于 let, reactive 类似于 const,他们的作用场景不一样。

    shallowRef

    使用场景

    只需要监测对象的替换,不需要监测对象的属性修改。原始数据类型shallowRefref的效果没有差别。

    const p1 = {name: "hehe"};
    const p2 = {name: "xixi"};
    
    // 响应式数据
    const person = shallowRef(p1);
    
    person.value.name = "haha"; // 不会监测到数据变化
    person.value = p2;          // 会监测到数据变化
    

    reactive不存在替换对象的情况,所以shallowReactive是能监测到外部属性的变化,不能监测到内部属性的变化。

    实现原理

    • shallowRef不会将对象转换成reactive对象,只有value值变化后才会分发依赖。
    shallowRef

    你可能会好奇,person.value.name = "haha"设置新值后hasChanged(newVal, this._rawValue)不是应该true分发依赖吗?

    其实person.value.name = "haha"这里调用的是get方法,调用的是get方法,调用的是get方法。和set方法没有关系哦~~~

    什么时候调用set方法?当然是person.value = p2;这个方法啦。 希望没有被绕晕啊~~~

    isRef

    使用场景

    判断一个对象是否是ref对象

    实现原理

    • isRef很简单,就是判断__v_isRef是否为true。因为RefImpl__v_isRef就是true
    isRef

    unref

    使用场景

    获取ref对象的_value值,有可能是reactive对象(因为不是获取_rawValue的值)。

    实现原理

    unref

    toRef

    使用场景

    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;  // 修改张无忌的年龄
    

    这个API的主要功能是当只需要操作响应式数据的部分数据时,将部分数据提取成为一个ref对象,然后方便操作。例子中如果要操作张无忌的年龄得使用zhangshanfeng.child.child.age += 10,比较繁琐。

    这个接口也比较适合网络请求的返回值的处理,可能在某些请求中只有一部分数据是需要展示的,这部分提取出来处理就行了。

    实现原理

    • ObjectRefImpl处理objectkey;
    ObjectRefImpl
    • get就是取objectkey属性的值, set就是设置objectkey属性的值。由于object响应式对象,所以其实调用的就是响应式对象getset方法。
    ObjectRefImpl

    toRefs

    使用场景

    reactive响应式对象的每个属性创建一个ref对象,方便赋值和取值。

    const zhangshanfeng = reactive({
      name: '张三丰',
      age: 100,
      child: {
        name: '张翠山',
        age: 40,
        child: {
          name: '张无忌',
          age: 20
        }
      }
    })
    
    const refs = toRefs(zhangshanfeng);  // 获得张三丰的所以属性的refs。
    
    // 结果
    {
        name: <ObjectRefImpl>{_object: zhangsanfeng, key: "name"},
        age: <ObjectRefImpl>{_object: zhangsanfeng, key: "age"},
        child: <ObjectRefImpl{_object: zhangsanfeng, key: "child"},
    }
    

    实现原理

    • 就是对每个属性分别执行toRef调用
    toRefs

    customRef

    自定义一个ref对象,实现自己的功能。

    下面官方给的一个防抖的例子:get方法就是返回值,set方法是延迟200毫秒才设置值,在这200毫秒如果设置了新值,就重新计时200毫秒再赋值。

    function useDebouncedRef(value, delay = 200) {
      let timeout
      return customRef((track, trigger) => {
        return {
          get() {
            track()
            return value
          },
          set(newValue) {
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()
            }, delay)
          }
        }
      })
    }
    

    实现原理

    • customRef的参数tracktrigger分别是() => trackRefValue(this)() => triggerRefValue(this),可以收集依赖和分发依赖,customRef持有返回的对象的getset方法,这两个方法就是真正执行的赋值和取值的方法。
    CustomRefImpl

    相关文章

      网友评论

          本文标题:Vue3.0 常用响应式API的使用和原理分析(二)

          本文链接:https://www.haomeiwen.com/subject/iqjywltx.html