In order to be able to use the Composition API, we need to have a place where we can actually use it. In the VUE component, we refer to this location as setup

The setup function

The setup function is executed before the component is created, and the props in the setup function, once resolved, serve as the entry to the Composition API

Note: We can’t use this in the setup function because it won’t find the component instance. The reason is that although the component instance is created before the setup is performed, data,computed,methods, etc., are not resolved before the setup function is called, so they cannot be retrieved in the setup.

Setup takes two parameters (props,context)

props

As the first argument in the setup function, the props is responsive and will be updated when a new prop is passed in

Properties passed by the parent component will be put into the props object

  • The type of props is defined in the props option object, as in Vue2
  • The template is still written the same way
  • Because props are passed directly as arguments to the setup function, we can use them directly as arguments.
export default {
    props: {message: {type:String.default:"hello"}},setup(props){
        consloe.log(props.message)
    }
}
Copy the code

!!!!! Due to thepropsIt’s responsive, so we can’t use deconstruction, which would eliminate the responsiveness of props.

If you need structural props, then you need to use the toRefs function to complete the operation.

import {toRefs} from 'vue'
setup(props){
    const message = toRefs(props,'message')
    console.log(message.value)
}
Copy the code

context

As the second argument to the setup function, the context is a plain JavaScript object that exposes the component’s three properties

  • **attrs ** : all non-prop attributes
  • Slots: slots passed from the parent component
  • Emit: emit when we need to emit events internally. It is the same as this.$emit in vuE2

!!!!! Since the context is just a normal JavaScript object, it is not responsive, which means that we can safely comparecontextUse deconstruction

export default{
    setup(props,{attrs,slots,emit}){... }}Copy the code

The return value of the setup function

Since setup is a function, it should return a value

If the return value of setup is an object, then the property of that object is accessible in the template.

Note that refs returned from setup are automatically shallowly unpacked in the template, so there is no need to use.value in the template

Apis for responsiveness

reactive

Returns a responsive copy of the object

If you want to define data that is reactive in your setup, you can use reactive

const state = reactive({ name:'wangpf'.age:18 })
Copy the code

Why is it that reactive makes it reactive?

  • Because when we use the reactVie function to process our data, the data will be used again for dependency collection
  • All the dependencies collected are reactive operations on the object when the data changes
  • In fact, in VUE2, the data option that we write is given internallyreactiveFunction to turn it into a reactive object

Two points to note:

  • Reactive will unpack all deep refs while maintaining the refs’ responsiveness

    const count = ref(0)
    const obj = reactive({ count })
    
    // Ref will be unpacked
    console.log(count.value === obj.value) // true
    
    // Update to 'obj.count'
    count.value++ 
    console.log(count.value) / / 1
    console.log(obj.count) / / 1
    
    // Update to ref 'count'
    obj.count++
    console.log(count.value) / / 2
    console.log(obj,value) / / 2
    Copy the code
  • When the REF is allocated to reactive, it will automatically unpack

    const count = ref(1)
    const obj = reactive({ })
    obj.count = count
    console.log(obj.count) / / 1
    console.log(obj.count === count.value) // true
    Copy the code

But because reactive is limited to the type we pass in, it requires that we pass in an object or an array

If we pass in a basic data type (String,Number,Boolean), we will get a warning.

In this case, we can use another API,ref

ref

Accepts an internal value and returns a responsive and variable REF object. The ref object has a single property (.vlaue) that points to an internal value

Ref returns a mutable responder object that maintains its internal value as a responder reference.

The internal values are maintained in the Vlaue attribute of the REF

readonly

We can get a reactive object by using reactive or ref, but in some cases we pass in a reactive object that we want to use somewhere else, but can’t be modified, we can use readonly

Readnoly returns a read-only proxy for a native object by hijacking it in the set method using the set in the proxy, and setting the value cannot be modified

API on reactive judgment

isProxy

Check whether the object is a proxy created by reactive or readonly

const state = reactive({ count : 0 })
const count = 0
isProxy(state) // true
isProxy(readonly(state)) // true
isProxy(count) // false
Copy the code

isReactive

Check whether the object is the corresponding mobile agent created by reactive

But if the proxy was created by readonly but wrapped around another proxy created by reactive, it will also return true

const state = reactive({ count : 0 })
isReactive(state) // true
isReactive(readonly(state)) // true
Copy the code

isReadOnly

Check whether the object is a read-only agent created by readonly

toRaw

Return the original object of the reactive or readonly proxy. (Use it with caution because persistent references to the original object are not recommended.)

shallowReactive

Shallow (shallow)

Create a responsive proxy that tracks the responsiveness of its own property, but does not perform deep responsive transformations of nested objects (deep versus native)

Similar to shallow copy, only the first layer is made responsive, and the deep layer is still the original object

shallowReadonly

Create a proxy that makes its own property read-only, but does not perform deep read-only transformations of nested objects

That means the first layer is read-only, but the deeper layer is still readable and writable

toRefs

Since we use ES6’s destructive syntax to destruct and assign values to objects returned by reactive, the destructed data is not responsive

And using toRefs, we can convert all the properties in the object that we return from reative to ref so that all the data that we deconstruct again is ref.

const state = reactive({ name:'wangpf' , age:18 });
const { name, age } = state; // The data is unresponsive

const { name, age } = toRefs(state) // This deconstructed data is converted to ref, which is responsive
// This creates a connection between state.name and name.value. Modifying either will cause the other to change
Copy the code

toRef

If we want to convert the properties of a reactive object toRef, we can use the toRef method

const state = reactive({ name:'wangpf' , age:18 });
const name = toRef(state,"name"); // The name is ref,
// Similarly, a connection is established between name.value and state.name, and the changes will affect each other
Copy the code

Ref other apis

unref

If you want to get the value in a ref reference, you can use the unref method:

  • If the parameter is a ref, return the internal value, otherwise return the parameter value

  • It’s actually a grammatical sugar:

    • val = isRef(val) ? val.value : val
      Copy the code
function useFoo(x: number | Ref<number>) {
  const unwrapped = unref(x) // unwrapped must be numeric now
}
Copy the code

isRef

Determines if the value is a ref object

shallowRef

Create a shallow ref object

const info = shallowRef({ name: "wangpf" })

const changeInfo  = () = > {
    info.value.name = 'wpf'  // The modification is not responsive
}
Copy the code

triggerRef

Manually triggers side effects associated with shallowRef

const info = shallowRef({ name: "wangpf" })

const changeInfo  = () = > {
    info.value.name = 'wpf'  // The modification is not responsive
    TriggerRef = triggerRef
    triggerRef(info)
}
Copy the code

customRef

Create a custom REF and display control over its dependency tracking and update triggers

  • It requires a factory function that takes the track and trigger functions as parameters
  • It should return an object with get and set
An example of stabilization with customRef
import { customRef } from "vue";

export default function (value, delay = 300) {
  let timer = null;
  return customRef((track, trigger) = > {
    return {
      get() {
        track();
        return value;
      },
      set(newValue) {
        clearTimeout(timer);
        timer = setTimeout(() = >{ value = newValue; trigger(); }, delay); }}; }); }Copy the code
<template> <input type="text" v-model="message" /> <p>{{ message }}</p> </template> <script> import useDebounceRef from ".. /hooks/useDebounceRef"; export default { name: "Demo2", setup() { const message = useDebounceRef("hello", 300); return { message, }; }}; </script>Copy the code

computed

The API’s method is the same as vuE2’s, except that it is written in the setup function, but note that the value returned for computed is a ref

  • Method one: receive a getter function and return the value for the getter, returning an invariant ref object
    const firstName = ref("wangpf");
    const lastName = ref("ok");
	const fullName = computed(() = > `${firstName.value} ${lastName.value}`)
Copy the code
  • Mode two: receive an object with get and set and return a variable (read and write) REF object
    const firstName = ref("wangpf");
    const lastName = ref("ok");
    const fullName = computed({
      get() {
        return `${firstName.value} ${lastName.value}`;
      },
      set(newValue) {
        const names = newValue.split("");
        firstName.value = names[0];
        lastName.value = names[1]; }});const changeName = () = > {
      fullName.value = "wpf err";
      console.log(fullName.value);
    };
Copy the code

Listen for data changes (Watch watchEffect)

In the Composition API, we can use watchEffect and Watch to do responsive data listening

  • Watch needs to manually specify the data source to listen to
  • WatchEffect is used to automatically collect dependencies for responsive data

watchEffect

Responsive to calculate and listen | Vue. Js (vuejs.org)

WatchEffect is used when we listen for some responsive data change and want to do something

Here’s a case:

const name = ref('wangpf')
const age = ref(18)

watchEffect(() = > {
    console.log('watchEffect executed ',name.value,age.value)
})
Copy the code
  • With the above code, the first function passed in watchEffect is executed immediately, and dependencies are collected during execution
    • (The reason why you need to do it now is to collect dependencies.)
  • Second, the function passed in by watchEffect is executed only if the collected dependencies change
WatchEffect stop listening
  • If, at some point, we want to stop listening, we can get the watchEffect return value function and call it

Here’s an example:

const stopWatch = watchEffect(() = > {
    console.log('watchEffect executed ',name.value,age.value)
})
const changeAge = () = > {
    age.value++;
    if(age.value > 20){
        stopWatch() // To stop listening, discard the watchEffect return value}}Copy the code
WatchEffect Removes side effects

Use: for example, in development, we need to perform a network request in the listener function, but we stopped the listener before the network request arrived, or the listener function has been executed again, and we need to cancel the last network request

Here’s an example:

watchEffect((onInvalidate) = > {
    console.log('watchEffect executed ',name.value,age.value)
    const timer = setTimeout(() = > {
        console.log('Operations performed after 1s')},1000)
    onInvalidate(() = > {
        // Do some cleanup here
        clearTimeour(timer)
    })
})
Copy the code

In the code above, the function we passed in to watchEffect is called back with one parameter: onInvalidate (which is a function), in which we can perform some cleanup.

WatchEffect execution time (refresh time)

By default, component updates are performed before the watchEffect (side effect function)

  const title = ref(null);  // The title is bound to the div tag
    watchEffect(() = > {
      console.log(title.value);
    });
    return { title };
Copy the code

So, when we get the element in watchEffect, the first execution is definitely null, it’s not allowed. Only after the DOM is mounted will the title be assigned a new value, and the watchEffect is executed again to print out the corresponding element

If we want to print the element the first time, we need to pass a second argument to watchEffect, which is an object with three values flush: ‘pre’ (default), ‘POST’, and ‘async’ (not recommended)


// Triggers after the component is updated, so you can access the updated DOM.
// Note: This will also delay the initial run of the side effects until the first rendering of the component is complete.
watchEffect(
  () = > {
    / *... * /
  },
  {
    flush: 'post' // Execute immediately after the DOM element is mounted or updated. "pre" executes immediately (default)})Copy the code

watch

The Watch API works the same way as watch in vUE 2’s Option API. By default, Watch only calls back when the listened source sends a change

Compared with watchEffect, the differences are:

  • Watch is a side effect of lazy execution
  • More specifically, what state should trigger a listener to restart
  • The values before and after the state change can be accessed
Listening to a single data source

There are two ways to listen to a single data source: pass in a getter function or a ref object

/ / listen for the getter
const state = reactive({ name : 'wangpf' })
watch(() = > state.name , (newVal,oldVal) = > { 
	/ *... * /
})

/ / listen for ref
const name = ref('wamgpf')
watch(name,(newVal,oldVal) = > { 
  / *... * /
  // Where newVal,oldVal are the values of the returned ref.value
})
Copy the code
Listening to multiple data sources

Method: Pass in an array

    const firstName = ref("AAA");
    const lastName = ref("bbb");
    const changeName = () = > {
      firstName.value = "A";
      lastName.value = "b";
    };
    watch([firstName, lastName], (newVal, oldVal) = > {
      console.log("newVal:", newVal, "oldVal:", oldVal);
    });
// newVal: ["A", "b"] oldVal: ["AAA", "bbb"]
Copy the code
Listen for responsive objects

Listen to reactive objects

const numbers = reactive([1.2.3.4])

watch(
  () = > [...numbers],
  (numbers, prevNumbers) = > {
    console.log(numbers, prevNumbers)
  }
)

numbers.push(5) / / logs: [1, 2, 3, 4, 5] [1, 2, 3, 4]
Copy the code

If you want to listen deeply on nested objects or arrays, you need to set deep to true,

const state = reactive({ 
  id: 1.attributes: { 
    name: ' ',
  }
})

watch(
  () = > state,
  (state, prevState) = > {
    console.log(
      'not deep',
      state.attributes.name,
      prevState.attributes.name
    )
  }
)

watch(
  () = > state,
  (state, prevState) = > {
    console.log(
      'deep',
      state.attributes.name,
      prevState.attributes.name
    )
  },
  { deep: true }
)

state.attributes.name = 'Alex' // Log: "deep" "Alex" "Alex"
Copy the code

But you’ll find that the new value is the same as the old one. This is where deep copy is needed for full listening

Other apis

Life cycle function

em…. Go look at the documentation,

Life cycle hook | Vue. Js (vuejs.org)

Provide / Inject

The function is the same as before,

We can provide data with provide

  • Each property is defined through the provide method
  • Pass two arguments: name (the supplied property name) and value (the supplied property value)

We can use JNject to inject what we want

  • The name of the property to be injected
  • Default value (Optional)
let count = ref(100)
let info = { name : "wangpf" , age : 18 }
provide("count",readonly(count))
provide("info",readonly(info))   // It is recommended to use readonly to wrap the value so that the data passed will not be changed by the inject component

// Obtain it from the descendant component via inject
const count = inject("count")
const info = inject("info")
Copy the code

H function

Before generating the real DOM, VUE will convert our nodes into vnodes, and the vnodes will form a tree structure, namely the virtual DOM

The HTML in the Template is the corresponding VNode generated using the render function

If we wanted to write the createVNode function in JavaScript to generate the corresponding VNode, we would use the h() function

The h() function is a common VNode function. The more accurate name is the createVNode() function, but vue has simplified it to h() for simplicity

The h() function takes three arguments: (tag, attribute, offspring)

h(
  // {String | Object | Function} tag
  // An HTML tag name, a component, an asynchronous component, or
  // A functional component.
  //
  // Necessary.
  'div'.// {Object} props
  // The object corresponding to attribute, prop, and event.
  // We will use it in the template.
  //
  // Optional.
  {},

  // {String | Array | Object} children
  // Subvnodes, built with 'h()',
  // Or get a "text Vnode" or using a string
  // Objects with slots.
  //
  // Optional.
  [
    'Some text comes first.',
    h('h1'.'A headline'),
    h(MyComponent, {
      someProp: 'foobar'})])Copy the code

Note: If there are no props, we can pass children as the second argument, but that would be ambiguous, so we would normally pass null as the second argument and children as the third

Basic use of the h function

  • This can be used in the Render function option
  • Can be used in the Setup function option
export default {
    render(){
        return h('div', { class:'app' }, 'hello app')}}export default {
    setup(){
        return () = > h('div', { class:'app' }, 'hello app')}}Copy the code

JSX is recommended. It has the same syntax as React, so I won’t go into details here. To see the document

jsx | Vue.js (vuejs.org)

Custom instruction

The custom command | Vue. Js (vuejs.org)

In Vue, the main form of code reuse and abstraction is components. However, in cases where you still need low-level manipulation of normal DOM elements, custom directives are used.

There are two types of custom instructions:

  • Custom local directives: Using the Directives option in a component can only be used in the current component.
  • Custom global directives: The app directive method can be used in any component.

The life cycle of the instruction

An object defined by a directive, Vue provides the following hook functions:

  • Created: called before the attribute or event listener of the bound element is applied;
  • BeforeMount: called when a directive is first bound to an element and before the parent component is mounted;
  • Mounted: called after the parent component of the bound element is mounted.
  • BeforeUpdate: called before updating the VNode containing components;
  • Updated: called after the VNode that contains the component and its child components have been updated;
  • BeforeUnmount: called before the parent component of the bound element is unmounted;
  • Unmounted: called only once when the directive is unbound from the element and the parent component has been unmounted

These hook functions take four arguments: el, binding, vnode, and prevNnode

Example: time formatting instruction

Teleport

Teleport | Vue.js (vuejs.org)

Teleporting components that can be moved to other DOM elements.

Usually used to encapsulate modal boxes, toast, etc., place it on the Body element at the level of the div#app element

The two properties

  1. To: Specifies the target element to which to move its contents, using a selector
  2. Disabled: Indicates whether to disable the teleport function
<teleport :to='#demo'>
    <h2>hello</h2>
</teleport>

// The element will be transferred to the element with id demo
Copy the code

Vue plug-in

Plugins | Vue. Js (vuejs.org)

Typically, we use the plug-in pattern when adding functionality to Vue globally

Two ways to write it

  1. Object type
    • aobject, but must include oneinstallThe function,This function is executed when the plug-in is installed
  2. Function types
    • A function that is automatically executed when the plug-in is installed
// plugin_obect.js
export default {
  install(app) {
    app.config.globalProperties.$name = "wangpf"; }};// main.js
import plugin_object from "./plugins/plugin_object";
app.use(plugin_object);

// app.vue 
import { getCurrentInstance } from "vue";
 setup() {
    const Instance = getCurrentInstance();
    console.log("Instance", Instance.appContext.config.globalProperties.$name);
 }
Copy the code