• Author: Chen Da Yu Tou
  • Making: KRISACHAN

As a new feature Composition API, it was released some time before Vue3 was officially released.

As described in the document, Composition APIS are a set of less intrusive, functional apis that allow you to “compose” a component’s logic more flexibly.

Not only in Vue, but also in other frameworks and native JS, let’s take a look at some of the more important Composition apis and walk through some simple examples to see how they can be used in other projects.

Note: This article only lists the most important apis under each category. If you want to see all of them, you can click the link below to view them:

Github.com/vuejs/vue-n…

reactive API

createReactiveObject

The createReactiveObject function is the core of the Reactive API and is used to createReactive objects.

Before sharing the API, let’s take a look at its core implementation. Due to space constraints, this article only shows a simplified version of the understood API, which looks like this:

function createReactiveObject(
  target, // Listen on the target
  isReadonly, // Whether it is read-only
  baseHandlers, // Target specifies the processor used when the target is Object or Array. It supports adding, deleting, modifying, and querying data
  collectionHandlers // Target Is the processor when the value is Map/WeakMap or Set/WeakSet, which supports adding, deleting, modifying, and checking data
) {
    if(target[ReactiveFlags.RAW] && ! (isReadonly && target[ReactiveFlags.IS_REACTIVE]) {// If target is already a Proxy, return it directly
      // Exception: call readonly() from Proxy
    	return target
    }
    // If the current object is no longer being listened on, return the object being listened on directly
    if (existingProxy) {
      return existingProxy
    }
    // If it is an Object Array Map/WeakMap Set/WeakSet, only value data can be monitored, and directly returned
    if (targetType === TargetType.INVALID) {
      return target
    }
    // Generate the corresponding Proxy object according to the parameter type and add the corresponding processor
    const proxy = new Proxy(
      target,
      targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
    )
    proxyMap.set(target, proxy)
    return proxy
}
Copy the code

reactive

A responsive proxy that takes a normal object and returns that normal object. Vue.observable() equivalent to 2.x

The following is an example:

import {
  reactive,
  isReactive // Check if it is a reactive object
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
const obj = {
  nested: {
    foo: 1
  },
  array: [{ bar: 2}}]const value = 10

const observedObj = reactive(obj)
const observedValue = reactive(value)

console.log(isReactive(observedObj)) // true
console.log(isReactive(observedValue)) // true
Copy the code

shallowReactive

Create shallow, responsive proxies for private (layer 1) properties of an object, not deep, recursive, responsive proxies for “properties of properties,” but just leave them as they are.

The following is an example:

const obj = {
  nested: {
    foo: 1
  },
  array: [{ bar: 2}}]const value = 10

const unobservedObj = shallowReactive(obj)
const unobservedValue = shallowReactive(value)

console.log(isReactive(observedObj)) // false
console.log(isReactive(observedValue)) // false
Copy the code

effect API

createReactiveEffect

CreateReactiveEffect is the core of the Effect API and is used to create functions that listen to user-defined Reactive objects

Before sharing the API, let’s take a look at its core implementation. Due to space constraints, this article only shows a simplified version of the understood API, which looks like this:

function createReactiveEffect(
	fn, // A user created reactive object changes to perform a callback
  options = {
    lazy, //Whether the user function scheduler is executed,//Collect data and record onTrack,//OnTrigger, a callback that tracks user data,//Trace the change record onStop,//Stop allowRecurse//Whether recursion is allowed}) {
    const effect = function reactiveEffect() {
      if(! effectStack.includes(effect)) { cleanup(effect)/ / clear the effect
        try {
          enableTracking() // Add current effect to the stack that tracks user data
          effectStack.push(effect) // Add effect to effect stack
          activeEffect = effect // Change activity effect to current effect
          return fn() // Perform the callback
        } finally { // Delete the current record
          effectStack.pop()
          resetTracking()
          activeEffect = effectStack[effectStack.length - 1]
        }
      }
    }
		effect.id = uid++
    effect._isEffect = true
    effect.active = true
    effect.raw = fn
    effect.deps = []
    effect.options = options
    return effect
}
Copy the code

effect

The Effect function is the core of the Effect API. WeakMap is the data type, which is a subscriber used to store user-defined functions.

The following is an example:

let dummy
const counter = reactive({ num: 0 })
effect(() = > (dummy = counter.num))
console.log(dummy === 0) // true
counter.num = 7
console.log(dummy === 7) // true
Copy the code

ref API

RefImpl

RefImpl is the core of the REF API and is used to create ref objects

Before sharing the API, let’s take a look at its core implementation, which looks like this:

class RefImpl {
  private _value // Store the value of the current ref object

  public __v_isRef = true // Determine if it is a ref object variable (read only)

  constructor(
    private _rawValue, // The original value passed in by the user
    public readonly _shallow = false // Whether the current ref object is shallowRef
  ) {
    // convert: If the original value passed in is an object, it is converted to a Reactive object by the convert function
    this._value = _shallow ? _rawValue : convert(_rawValue)
  }

  get value() {
    // Used to track changes in user input values
    // track: The track function of the Effect API, which tracks user behavior, currently tracks user GET operations
    // toRaw: Effect API toRaw function that converts this to user input
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }

  set value(newVal) {
    if (hasChanged(toRaw(newVal), this._rawValue)) {
      // When the current ref object changes
      // _rawValue / _value changed
      // trigger: The trigger function of the effect API operates based on the value and action type passed in by the user. Currently, it adds the value passed in by the user to the value map
      this._rawValue = newVal
      this._value = this._shallow ? newVal : convert(newVal)
      trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
    }
  }
}
Copy the code

ref

Accepts a parameter value and returns a reactive and mutable REF object. The ref object has a single.value attribute that points to an internal value. If an object is passed to a REF, the reactive methods are called for the deep response transformation.

The following is an example:

const count = ref({
  name: 'head'.type: 'handsome boy'
})
console.log(count.value.type) / / a handsome boy
count.value.type = 'Super handsome'
console.log(count.value.type) // He is very handsome
Copy the code

shallowRef

A ref is created and its.value changes are tracked, but reactive proxy transformations are not performed on the changed.value.

The following is an example:

const __shallowRef = shallowRef({ a: 1 })
let dummy
effect(() = > {
  dummy = __shallowRef.value.a
})
console.log(dummy) / / 1

__shallowRef.value.a = 2
console.log(dummy) / / 1
console.log(isReactive(__shallowRef.value)) // false
Copy the code

customRef

CustomRef is used to define a ref that explicitly controls the dependency trace and trigger response, takes a factory function with track for the trace and trigger for the response, and returns an object with get and set attributes.

The following is an example:

let value = 1
let _trigger

const custom = customRef((track, trigger) = > ({
  get() {
    track()
    return value
  },
  set(newValue) {
    value = newValue
    _trigger = trigger
  }
}))

let dummy
effect(() = > {
  dummy = custom.value
})
console.log(dummy) / / 1

custom.value = 2
console.log(dummy) / / 1

_trigger()
console.log(dummy) / / 2
Copy the code

triggerRef

TriggerRef is used to trigger shallowRef actively

The following is an example:

const __shallowRef = shallowRef({ a: 1 })
let dummy
effect(() = > {
  dummy = __shallowRef.value.a
})
console.log(dummy) / / 1

__shallowRef.value.a = 2
console.log(dummy) / / 1
console.log(isReactive(__shallowRef.value)) // false

triggerRef(__shallowRef)
console.log(dummy) / / 2
Copy the code

computed API

ComputedRefImpl

ComputedRefImpl is the core of the REF API for creating computed objects

Before sharing the API, let’s take a look at its core implementation. Due to space constraints, this article only shows a simplified version of the understood API, which looks like this:

class ComputedRefImpl {
  private _value / / the current value
  private _dirty = true // Whether the current value has changed

  public effect / / object effect

  public __v_isRef = true; // Specify the ref object
  public [ReactiveFlags.IS_READONLY]: boolean // Whether it is read-only

  constructor(
    getter, // getter
    private _setter, // setter
    isReadonly // Whether it is read-only
  ) {
    this.effect = effect(getter, {
      lazy: true.scheduler: () = > {
        if (!this._dirty) {
          // Change the change status to true
          // trigger: The trigger function of the effect API operates based on the value and action type passed in by the user. Currently, it adds the value passed in by the user to the value map
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')}}})this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    if (this._dirty) {
      // Return current value
      // Change the change status to false
      this._value = this.effect()
      this._dirty = false
    }
   	// track: The track function of the Effect API, which tracks user behavior, currently tracks user GET operations
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }

  set value(newValue) {
    this._setter(newValue)
  }
}
Copy the code

computed

Pass in a getter function that returns a ref object that cannot be manually modified by default. Or pass in an object with get and set functions to create a computed state that can be manually modified.

The following is an example:

const count1 = ref(1)
const plus1 = computed(() = > count1.value + 1)
console.log(plus1.value) / / 2
plus1.value++ // Write operation failed: computed value is readonly

const count2 = ref(1)
const plus2 = computed({
  get: () = > count2.value + 1.set: val= > {
    count2.value = val - 1}})console.log(plus2.value) / / 2
plus2.value = 0
console.log(plus2.value) / / 0
Copy the code