- 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