zoukankan      html  css  js  c++  java
  • vue3.x源码学习 Ref 学习笔记1

    ref

    ref最重要的作用,其实是提供了一套Ref类型,什么是ref类型呢?

    ref类型

    export interface Ref<T = any> {
      value: T
      /**
       * Type differentiator only.
       * We need this to be in public d.ts but don't want it to show up in IDE
       * autocomplete, so we use a private Symbol instead.
       */
       // 用此唯一key,来做Ref接口的一个描述符,让isRef函数做类型判断
      [RefSymbol]: true
      /**
       * @internal
       */
      _shallow?: boolean
    }
    

    对于基本数据类型,函数传递或者对象解构时,会丢失原始数据的引用,换言之,我们没法让基本数据类型,或者解构后的变量(如果它的值也是基本数据类型的话),成为响应式的数据。

    // 我们是永远没办法让`a`或`x`这样的基本数据成为响应式的数据的,Proxy也无法劫持基本数据。
    const a = 1;
    const { x: 1 } = { x: 1 }
    

    但是有时候,我们确实就是想一个数字、一个字符串是响应式的,或者就是想利用解构的写法。那怎么办呢?只能通过创建一个对象,也即是源码中的Ref数据,然后将原始数据保存在Ref的属性value当中,再将它的引用返回给使用者。既然是我们自己创造出来的对象,也就没必要使用Proxy再做代理了,直接劫持这个value的get/set即可,这就是ref函数与Ref类型的由来。

    isRef判断

    如何判断是否是ref类型,下面是源代码,还是很简单的。

      // 判断是否是Ref
    export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
    export function isRef(r: any): r is Ref {
      return Boolean(r && r.__v_isRef === true)
    }
    

    创建Ref

    那么是怎么创建ref呢?下面我们就讲讲创建ref。

    function createRef(rawValue: unknown, shallow = false) {
      if (isRef(rawValue)) {
        return rawValue
      }
      return new RefImpl(rawValue, shallow)
    }
    

    RefImpl构造函数

    这里也定义了get/set,没有任何Proxy相关的操作。

    class RefImpl<T> {
      private _value: T
    
      public readonly __v_isRef = true
    
      constructor(private _rawValue: T, public readonly _shallow = false) {
          // 如果是对象就value就是reactive(val),否则就是自己
        this._value = _shallow ? _rawValue : convert(_rawValue)
      }
      // 获取数据
      get value() {
          // 监听函数收集依赖的方法
        track(toRaw(this), TrackOpTypes.GET, 'value')
        return this._value
      }
      // 设置新的值 
      set value(newVal) {
        if (hasChanged(toRaw(newVal), this._rawValue)) {
          this._rawValue = newVal
          this._value = this._shallow ? newVal : convert(newVal)
          trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
        }
      }
    }
    

    convert源码

    const convert = <T extends unknown>(val: T): T =>
      isObject(val) ? reactive(val) : val
    

    这里引入了reactive,如果是对象就value就是reactive(val),否则就是自己,reactive暂时先不讲,放后面讲

    toRef&toRefs

    toRef也是创建get/set进行拦截,toRefs就是对数组或者对象的属性进行递归设置get/set。

    class ObjectRefImpl<T extends object, K extends keyof T> {
      public readonly __v_isRef = true
    
      constructor(private readonly _object: T, private readonly _key: K) {}
    
      get value() {
        return this._object[this._key]
      }
    
      set value(newVal) {
        this._object[this._key] = newVal
      }
    }
    
    export function toRef<T extends object, K extends keyof T>(
      object: T,
      key: K
    ): ToRef<T[K]> {
      return isRef(object[key])
        ? object[key]
        : (new ObjectRefImpl(object, key) as any)
    }
    
    export function toRefs<T extends object>(object: T): ToRefs<T> {
      if (__DEV__ && !isProxy(object)) {
        console.warn(`toRefs() expects a reactive object but received a plain one.`)
      }
      const ret: any = isArray(object) ? new Array(object.length) : {}
      for (const key in object) {
        ret[key] = toRef(object, key)
      }
      return ret
    }
    你要觉得这篇文章比较好,记得点推荐!
  • 相关阅读:
    Shell中调用java时的参数
    简析echo命令在Linux系统中的使用
    设置Linux环境变量的三种方法
    nohup 后台运行,以及重定向标准输出和标准错误 &/dev/null 文件
    &命令
    linux下卸载gij的java
    在Linux下运行可执行Jar包
    jar参数运行应用时classpath的设置方法
    shell获取当前进程pid和上一个进程pid
    检查文件,如果文件不存在则创建
  • 原文地址:https://www.cnblogs.com/yiyi17/p/14456871.html
Copyright © 2011-2022 走看看