In this article, we learn Vue3 responsive API part ref related source code implementation, only a deeper grasp of the relevant implementation principles and technical details, so that we can better use the framework for development in the project development application.

1. ref

Official description: Takes an internal value and returns a reactive and mutable REF object. The ref object has a single property.value pointing to an internal value

1.1 Purpose of ref

In VUe2, all of our responsive data is defined in the component data attribute. Even if it is not used now, it must be defined in the future. In vue3, the reactive modules have been completely rewritten. A composite API setup() has been added to improve the code style of optional API components in VUe2, and a reactive API () function has been added to handle Object data. Sometimes we need a simple value in the component to be reactive, such as number, string, etc., and this is a good time to use ref to deal with reactive.

1.2 Source code implementation

File definition: Packages \reactivity\ SRC \ref.ts

export function ref(value? : unknown) {
  return createRef(value)
}
Copy the code

The ref function definition is very simple

Description:

  1. Ref is defined as a function export
  2. Return to call creatRef()

CreatRef () function creatRef()

function createRef(rawValue: unknown, shallow = false) {
  if (isRef(rawValue)) {
    // Already ref proxy object, return original target object
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}
Copy the code

Description:

  1. CreateRef takes two arguments, rawValue raw and shallow proxy flag, which defaults to false
  2. First, it checks whether the value passed in is of type REF. If it is, it returns the value directly without any processing
  3. If it is not ref, the instance object calling the RefImpl class is returned

RefImpl class implementation:

class RefImpl<T> {
  private _value: T
  // isRef,isRef,isRef
  public readonly __v_isRef = true

  constructor(private _rawValue: T, public readonly _shallow = false) {
    this._value = _shallow ? _rawValue : convert(_rawValue)
  }
  // Get data
  get value() {
    // Rely on collection
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }
  // Set the new value
  set value(newVal) {
    // Determine whether there is a change between the old and new values
    if (hasChanged(toRaw(newVal), this._rawValue)) {
      this._rawValue = newVal
      this._value = this._shallow ? newVal : convert(newVal)
      // Trigger dependencies
      trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
    }
  }
}
Copy the code

Description:

  1. The ref response here uses the GET set property of the ES6 class to proxy
  2. The RefImpl class defines a private attribute _value to hold old values, and a read-only attribute __v_isRef defaults to true for isRef type determination
  3. The constructor takes two parameters, the original value and whether the response is shallow
  4. The constructor internally stores the old value according to _shallow, and the shallow proxy directly stores the original value, otherwise storing the return value from calling covert()
  5. Get Gets data for collecting dependencies
  6. If there is no change, it will return directly without triggering dependency operation. This is performance optimization method. If there is a change in the old and new values, it will modify the value and save it to trigger dependency update rendering.

The convert() function implements:

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

Here we see that the convert function is handled internally by determining the type of the value passed in, or by calling reactive() if it is of type Object, or by returning the value itself if it is simple.

1.3 Summary of ref
  1. Reactive processing of simple values is intercepted using es6’s Class Get Set, while complex data is implemented by Reactive ()
  2. If a new value is changed, the system changes the new value to determine whether to update dependencies and optimize performance

2. shallowRef

Official description: Creates a ref that tracks changes in its.value, but does not make its value reactive as well

ShallowRef will only be its. Value value itself is reactive, and the corresponding value of. Value will not be reactive

2.1 Source code implementation
export function shallowRef(value? : unknown) {
  return createRef(value, true)}Copy the code

Description:

  1. The shallowRef implementation is basically the same as the ref implementation, except that the shalloa value is written to true when createRef is called

3. isRef

Official description: Check if the value is a ref object

3.1 Source code implementation
export function isRef(r: any) :r is Ref {
  // Determine if an object is a ref object using the __v_isRef attribute
  return Boolean(r && r.__v_isRef === true)}Copy the code

Checking the judgment is simple, using the internal properties of the RefImpl class

4. unRef

Official description: Returns the internal value if the argument is a ref, otherwise returns the argument itself.

4.1 Source code implementation
export function unref<T> (ref: T) :T extends Ref<infer V>?V : T {
  return isRef(ref) ? (ref.value as any) : ref
}
Copy the code

The internal implementation is still simple, by judging the incoming value and returning the ref. Value value itself if it is ref, otherwise returning the incoming value.

5. toRef

Can be used to create a new ref for a property on a source responsive object. The REF can then be passed, maintaining a reactive connection to its source property.

5.1 Source code implementation
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)
}
Copy the code

Description:

  1. The function takes two arguments, object and key
  2. First check whether object[key] is of type REF. If it is, the value of object[key] is directly returned. Otherwise, instantiate ObjectRefImpl, create a ref, and return

ObjectRefImpl class implementation

class ObjectRefImpl<T extends object.K extends keyof T> {
  // Read only ref type attributes
  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
  }
}
Copy the code

6. toRefs

Official description: Converts a reactive object to a normal object, where each property of the resulting object is a ref pointing to the corresponding property of the original object

6.1 Source code Implementation
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
}
Copy the code

Description:

  1. First check whether the passed object parameter is a proxy object
  2. If the object parameter is an array, create an empty array with the length of Object. length. If the array is an empty array, initialize an empty object
  3. Iterate over object and call toRef(Object,key)
  4. Returns an object or array type in the following format:
[ref, ref, ref]
or
{
    key1: ref,
    key2: ref
}
Copy the code