Analysis of Composition API in Vue 3

Analysis of Composition API in Vue 3

ref

Role: Define the responsiveness of a data (responsive data: data changes, and the page changes with rendering)

  • Generally used to define a basic type of responsive data

Ref is a function, and its role is to define a responsive data. It returns a Ref object. The object has a value attribute. If you need to manipulate the data, you need to use the Ref object to call the value attribute to perform data operations.

But in the htm template, the .value attribute is not needed

setup() { let x = ref(0); const counter = () => { x.value += 1; console.log(x.value); }; return {x, counter }; }, Copy code

reactive

  • Role: Define the responsiveness of multiple data
  • const proxy = reactive(obj): Receive a normal object and return the reactive proxy object of the normal object
  • Responsive conversion is "deep": it affects all nested properties inside the object
  • Internally based on ES6 Proxy implementation, the internal data of the source object is responsive through the proxy object operation

What it returns is a proxy object of Proxy, and the target object being proxied is

reactive()
Objects declared in parentheses.

The object being proxied by the following code is obj. user is the proxy object, and obj is the target object.

It is impossible to directly use the target object to update the value of the member in the target object. You can only use the proxy object to update the data (responsive data)

<script lang="ts"> import {defineComponent, reactive} from "vue"; export default defineComponent({ name: "Home", setup() { const obj = { name: "ade kang", age: 20, wife: { name: "tom", age: 18, cars: ["Benz", "BMW"], }, }; const user = reactive(obj); console.log(user); return {user }; }, }); </script> Copy code

The above code runs and prints out, it can be seen that the target object is some attributes in obj

For adding or deleting data in the proxy object and the target object, the interface data will be updated.

user.name = "jack"; obj.gender = "Male"; delete obj.age; user.gender = "Female"; Copy code

The above code will update the data of the page.

Vue3 responsive understanding

  • Through Proxy (proxy): Intercept any (13) operations on any attribute of data, including reading and writing of attribute values, adding attributes, deleting attributes, etc... You can view the MDN document: Proxy
  • Through Reflect (reflection): Dynamically perform specific operations on the corresponding attributes of the proxy object. You can view the MDN document: Reflect
<script> const user = { name: "ade kang", age: 20, wife: { name: "tom", age: 18, cars: [" ", " "], }, }; const proxyUser = new Proxy(user, { // get(target, prop) { console.log("get "); return Reflect.get(target, prop); }, /// set(target, prop, val) { console.log("set "); return Reflect.set(target, prop, val); }, deleteProperty(target, prop) { console.log("delete "); return Reflect.deleteProperty(target, prop); }, }); // console.log(proxyUser.name); // proxyUser.name = "jack long"; console.log(user); // proxyUser.gender = " "; console.log(user); // delete proxyUser.name; console.log(user); </script>

setup

Timing of execution of setup

  • Execute (once) before beforeCreate, at this time the component object has not been created

  • this is undefined, data/computed/methods/props cannot be accessed through this

  • In fact, all the callback functions related to the composition API are not available.

<script lang="ts"> import {defineComponent} from "vue"; export default defineComponent({ name: "child", props: { msg: String, }, beforeCreate() { console.log("beforeCreate executed"); }, setup() { console.log("setup executed"); }, }); </script> Copy code

We create a sub-component, and then call will remove the following phenomenon

If again

setup
If you use this to show off, you will report an error directly.

setup return value

  • The return value in setup is an object, and the internal properties and methods are used by the html template

  • Both the internal attributes of the object in setup and the attributes of the return object in the data function can be used in the html template

  • The properties of the object in the setup and the properties of the object in the data function will be merged into the properties of the component object

  • The methods in the object in the setup and the methods in the methods object will be merged into the method of the component object

  • If there is a duplicate name, setup takes precedence

note:

  • Generally do not mix: methods can access the attributes and methods provided by setup, but data and methods cannot be accessed in the setup method

  • setup cannot be an async function: because the return value is no longer the return object, but a promise, the template cannot see the attribute data in the return object

Setup parameters

  • setup(props, context)/setup(props, {attrs, slots, emit})

  • The props parameter is an object that contains the data passed by the parent component to the child component, and it is all the properties received using props in the child component

    The parameters accepted by context are

    attrs\slots/emit

  • attrs: objects containing attributes not declared in the props configuration, equivalent to this.$attrs

  • slots: An object containing the contents of all incoming slots, equivalent to this.$slots

  • emit: A function used to distribute custom events, equivalent to this.$emit

setup(props, context) { console.log("========"); console.log(props); console.log("-------"); console.log(context); }, Copy code

I just print out

setup
middle
props
with
context
. In this way, it is clear what is found.

reactive and ref-details

  • They are the two most important reactive APIs in the composition API of Vue3
  • Ref is used to process basic types of data, and reactive is used to process objects (recursive deeply responsive)
  • If you use ref objects/arrays, the objects/arrays will be automatically converted into reactive proxy objects internally
  • Inside ref: The hijacking of data is achieved by adding getter/setter to the value attribute
  • Internal reactive: Use Proxy to hijack all data inside the object, and manipulate the internal data of the object through Reflect
  • Ref data operation: .value is required in js, not required in the template (the .value is automatically added when the template is parsed internally)

Computing attributes and monitoring

computed function

If there is only one function on the calculated property, then it represents a getter, and it returns an object of type Ref.

setup() { const user = reactive({ firstName: "ade", lastName: "kang", }); //Realize the display of the first name by calculating attributes const fullName1 = computed(() => { return user.firstName + "_" + user.lastName; }); console.log(fullName1); return {user, fullName1 }; }, Copy code

It also supports setters. If getter and setter are passed in together, the wording is an object

const fullName2 = computed({ get() { return user.firstName + "_" + user.lastName; }, set(val) { console.log(val); const names = val.split("_"); user.firstName = names[0]; user.lastName = names[1]; }, }); Copy code

watch function

  • Consistent with watch configuration function

  • Monitor the specified one or more responsive data, once the data changes, automatically execute the monitoring callback

  • By default, the callback is not executed at the initial stage, but you can configure immediate to true to specify that it will execute the first time immediately at the initial stage

  • Specify deep monitoring by configuring deep to true

  • When monitoring non-responsive data in watch, add a callback to the data

    watch ([() => user.firstName , () => user.lastName, fullName], () => {}) copy the code

watchEffect function

  • There is no need to directly specify the data to be monitored, which reactive data is used in the callback function to monitor which reactive data

  • By default, it will be executed for the first time at the beginning, so that the data that needs to be monitored can be collected

  • Call back when monitoring data changes

toRefs

Convert a responsive object into an ordinary object, and each property of the ordinary object is a ref.

If you want to get the value in setup, you need

state.name.value

<script lang="ts"> import {defineComponent, reactive, toRefs} from "vue"; export default defineComponent({ name: "App", setup() { const state = reactive({ name: "adekang", age: "18", }); //const state2 = toRefs(state); const {name, age} = toRefs(state); setInterval(() => { //state.name += "-"; name.value += "="; }, 1000); //return {state }; //not responsive return {name, age }; }, }); </script> Copy code

ref to get elements

Use the ref function to get the label elements in the component

Functional requirements: let the input box automatically get the focus

<template> <h2>App</h2> <input type="text">--- <input type="text" ref="inputRef"> </template> <script lang="ts"> import {onMounted, ref} from'vue' export default { setup() { const inputRef = ref<HTMLElement|null>(null) onMounted(() => { inputRef.value && inputRef.value.focus() }) return { inputRef } }, } </script> Copy code

Composition API (other parts)

shallowReactive and shallowRef

  • shallowReactive: Only the responsive type (that is, shallow responsive) that handles the outermost properties of the object
  • shallowRef: Only the response of the value is processed, and the reactive processing of the object is not performed
  • When to use shallow responsiveness?
    • Under normal circumstances, you can use ref and reactive
    • If there is an object data, the structure is deeper, but only the outer attribute changes when it changes ===> shallowReactive
    • If there is an object data, a new object will be generated later to replace ===> shallowRef
<template> <h2>App</h2> <h3>m1: {{ m1 }}</h3> <h3>m2: {{ m2 }}</h3> <h3>m3: {{ m3 }}</h3> <h3>m4: {{ m4 }}</h3> <button @click="update">Update</button> </template> <script lang="ts"> import {reactive, ref, shallowReactive, shallowRef} from "vue"; export default { //eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types setup() { const m1 = reactive({ a: 1, b: {c: 2} }); const m2 = shallowReactive({ a: 1, b: {c: 2} }); const m3 = ref({ a: 1, b: {c: 2} }); const m4 = shallowRef({ a: 1, b: {c: 2} }); const update = () => { //Here each data is operated separately //m1.bc += 1; m2.bc += 1; //m3.value.a += 1; //m4.value.a += 1; }; return { m1, m2, m3, m4, update, }; }, }; </script> Copy code

readonly and shallowReadonly

  • readonly:
    • Deep read-only data
    • Get an object (responsive or pure object) or ref and return the read-only proxy of the original proxy.
    • Read-only proxies are deep: any nested properties accessed are also read-only.
  • shallowReadonly
    • Shallow read-only data
    • Create a proxy to make its own property read-only, but does not perform deep read-only conversion of nested objects
  • Application scenarios:
    • In some specific cases, we may not want to update the data, then we can wrap and generate a read-only proxy object to read the data, but cannot modify or delete

toRaw and markRaw

  • toRaw
    • Returned by
      reactive
      or
      readonly
      The method is transformed into a normal object of a reactive proxy.
    • This is a restore method that can be used for temporary reads, access will not be proxied/tracked, and interface updates will not be triggered when writing.
  • markRaw
    • Mark an object so that it will never be converted to a proxy. Return the object itself
    • Application scenarios:
      • Some values should not be set to be reactive, such as complex third-party class instances or Vue component objects.
      • When rendering large lists with immutable data sources, skipping proxy conversion can improve performance.
<template> <h2>{{state}}</h2> <button @click="testToRaw"> toRaw</button> <button @click="testMarkRaw"> markRaw</button> </template> <script lang="ts"> import { markRaw, reactive, toRaw, } from 'vue' export default { setup () { const state = reactive<any>({ name: 'tom', age: 25, }) const testToRaw = () => { const user = toRaw(state) user.age++ // } const testMarkRaw = () => { const likes = ['a', 'b'] //state.likes = likes state.likes = markRaw(likes)//likes setTimeout(() => { state.likes[0] += '--' }, 1000) } return { state, testToRaw, testMarkRaw, } } } </script>

toRef

  • ref , ,

  • ref: ,

  • : prop ref toRef

customRef

  • ref
  • : customRef debounce
<template> <h2>App</h2> <input v-model="keyword" placeholder=" "/> <p>{{keyword}}</p> </template> <script lang="ts"> /* : customRef debounce */ import { ref, customRef } from 'vue' export default { setup () { const keyword = useDebouncedRef('', 500) console.log(keyword) return { keyword } }, } /* Custom ref to realize function anti-shake */ function useDebouncedRef<T>(value: T, delay = 200) { let timeout: number return customRef((track, trigger) => { return { get() { //Tell Vue to track the data track() return value }, set(newValue: T) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue //Tell Vue to trigger the interface update trigger() }, delay) } } }) } </script> Copy code

provide and inject

  • Realize communication between cross-level components (grandchildren)
<template> <h1>Parent component</h1> <p>Current color: {{color}}</p> <button @click="color='red'">Red</button> <button @click="color='yellow'">yellow</button> <button @click="color='blue'">Blue</button> <hr> <Son/> </template> <script lang="ts"> import {provide, ref} from'vue' import Son from'./Son.vue' export default { name:'ProvideInject', components: { Son }, setup() { const color = ref('red') provide('color', color) return { color } } } </script> Copy code
<template> <div> <h2>Subcomponent</h2> <hr> <GrandSon/> </div> </template> <script lang="ts"> import GrandSon from'./GrandSon.vue' export default { components: { GrandSon }, } </script> Copy code
<template> <h3 :style="{color}">Grandchild component: {{color}}</h3> </template> <script lang="ts"> import {inject} from'vue' export default { setup() { const color = inject('color') return { color } } } </script> Copy code

Responsive data judgment

  • isRef: Check whether a value is a ref object
  • isReactive: Check whether an object is
    reactive
    Responsive proxy created
  • isReadonly: Check whether an object is made by
    readonly
    Read-only proxy created
  • isProxy: Check whether an object is made by
    reactive
    or
    readonly
    The proxy created by the method

Source: 24kcs.github.io/vue3_study/