I’ve written a blog post about the new features of Vue3, got a quick look at what Vue3 features are, and ended with a quick look at how to use the Compsition API in Vue3

In case you haven’t already heard about Vue3, check out Vue3’s new features

Because at the beginning of this month to make a small goal, learned the basic use of Vue3, and use Vue3 made a small project (say slightly, I made a small tool, is now 90% complete, at the end of this month will be through the form of blog to show before, and provide the design train of thought, everyone please look… In this article, Vue3 will be frequently compared to Vue2 to introduce Vue3, but also to the various API combined with code examples, this is not only a summary of their knowledge, but also hope to help you

One, foreword

As you all know, Vue3 versions have been released, and many teams are already working on various libraries and upgrades from Vue2 to Vue3, so we can’t be left behind, so upgrade your Vue2 to Vue3, and follow this article to learn the new API

To find out how to upgrade, click on the article at the beginning of this article. In the previous article, there was a nanny level tutorial on how to upgrade

Second, the body

Vue2 imports the entire Vue each time, such as the code in Vue2’s main.js file

import Vue from 'vue<br>
import App from './App.vue'<br>
Vue.config.productionTip = false<br>
new Vue({<br>
  render: h => h(App)<br>
}).$mount('#app')<br>
Copy the code

However, it is clear that we will not be able to use all of Vue’s apis in our project, so many modules will be useless

So in Vue3, there are a lot of exposed apis for developers to use. We can import the required APIS from Vue according to our needs. For example, the code in main.js

import { createApp } from 'vue';
import App from './App.vue'
createApp(App).mount('#app')
Copy the code

The import and export syntax of import and export is utilized to realize the function of packing modules on demand, and the file volume after the project is packaged is significantly smaller

This is why we need to look at the Vue3 API in detail in this article

(1) setup

The setup function is also the entry function to the Compsition API. Our variables and methods are defined in this function

< the template > < div id = "app" > < p > {{number}} < / p > < button @ click = "add" > add < / button > < / div > < / template > < script > / / 1. Import {ref} from 'vue' export default {name: 'App', setup() {// 2. Let number = ref(0) // 3. Function add() {// number is wrapped by the ref function, and its value is stored in.value number.value ++} // 4. Return {number, add}}} </script>Copy the code

The ref function is used in the above code, which will be explained in detail below, but you only need to understand that it is used to wrap a responsive data, and you can think of the variables wrapped by the ref function as those in Vue2 data

This simple implementation of a button click the number to add 1 function

In Vue2, we call variables in data or props using something like this.number, but it’s important to note that in setup, this refers to undefined, This means that you can no longer use this to obtain variables as in Vue2

So how do you get the props data?

The setup function has two parameters, props and context. The former contains the names and values of the parameters that the component is allowed to pass. The latter is a context object from which you can access ATTr, Emit, and slots

Emit is the familiar Vue2 communication method with the parent component, which can be directly invoked

(2) Life cycle

Vue2 provides life cycle functions such as beforeCreate, Created, beforeMount, Mounted, and beforeUpdate

In Vue3, these parts of the lifecycle have changed, and the way they are called has also changed. Here is a map of the changes to briefly understand them

Vue2	            Vue3
beforeCreate	    setup
created	            setup
beforeMount	        onBeforeMount
mounted	            onMounted
beforeUpdate	    onBeforeUpdate
updated	            onUpdated
beforeDestory   	onBeforeUnmount
destoryed	        onUnmounted
Copy the code

These lifecycle calls for Vue3 are also simple, as they are imported from Vue and then called directly

<template> <div id="app"></div> </template> <script> // 1. Import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue' export default { name: 'App', Setup () {onBeforeMount(() => {// Execute some code before mounting}) onMounted(() => {// Execute some code after mounting}) onBeforeUpdate(() => {// OnBeforeUnmount (() => {// Execute some code before component is destroyed}) unMounted(() => {// Execute some code before component is destroyed}) unMounted(() => {// Return {}}} </script>Copy the code

In particular, the setup function replaces the beforeCreate and created lifecycle functions, so we can think of its execution time between beforeCreate and created

(3) the reactive

The reactive method is used to create a reactive data object. This API also solves the problem of Vue2’s defineProperty implementation of reactive data

It’s easy to use, just pass in the data as a parameter, as shown below

<template> <div id="app"> <! {{state.count}} </div> </template> <script> // 1. Reactive import {reactive} from 'vue' export default {name: 'App', setup() {// 2. Const state = reactive({count: 3}) // 3. Return {state}}} </script>Copy the code

(4) the ref

When we introduced setup, we used the ref function to wrap a reactive data object, which on the surface looks exactly like reactive, and it does, because the ref is wrapping an object around reactive, Then we pass the value to the value property in the object, which explains why we need to add.value every time we access it

Reactive ({value: obj}) reactive(reactive: obj})

So let’s write a little bit of code here to look at that

<script>
import {ref, reactive} from 'vue'
export default {
  name: 'App',
  setup() {
   const obj = {count: 3}
   const state1 = ref(obj)
   const state2 = reactive(obj)

    console.log(state1)
    console.log(state2)
  }
  
}
</script>
Copy the code

Take a look at the printout

Note: the.value is required when you access the ref-wrapped object in the setup function. It is not required when you access the ref-wrapped object in the template, because at compile time, it is automatically identified if the object is ref-wrapped

So how do we choose reactive and ref?

Advice:

Primitive type values (String, Nmuber, Boolean, etc.) or single-valued objects (objects with only one property value, such as {count: 3}) use the ref reference type values (Object, Array) using reactive

(5) toRef

ToRef converts a value in an object to reactive data. It takes two parameters. The first parameter is an obj object. The second argument is the name of the property in the object

The code is as follows:

Import {toRef} from 'vue' export default {setup() {const obj = {count: 3} // 2. Const state = toRef(obj, 'count') // 3. Return {state}}} </script>Copy the code

But on the face of it, the toRef API seems pretty useless, because this function can also be implemented with ref, as shown below

<script> ref import {ref} from 'vue' export default {setup() {const obj = {count: 3} // 2. Const state = ref(obj. Count) // 3. Return {state}}} </script>Copy the code

At first glance, it seems to be true. In fact, there is a difference between the two. We can use an example to compare the two

< the template > < p > {{state1}} < / p > < button @ click = "add1" > add < / button > < p > {{state2}} < / p > < button @ click = "add2" > add < / button > </template> <script> import {ref, toRef} from 'vue' export default { setup() { const obj = {count: 3} const state1 = toRef(obj, 'count') const state2 = toRef(obj, 'count') function add1() {state1.value ++ console.log(' ', obj); Console. log(' reactive data object: ', state1); } function add2() {state2.value ++ console.log(' raw value: ', obj); Console. log(' reactive data object: ', state2); } return {state1, state2, add1, add2} } } </script>Copy the code

We convert the count in obj to reactive using ref and toRef, respectively, and declare two methods to increment the count value, respectively. After each increment, we print the original obj and the wrapped reactive data object, while watching the view change

Ref:

As you can see, after +1 is applied to the value of the reactive data, the view changes, the original value does not change, and the value of the reactive data object also changes. This indicates that the REF is a copy of the original data and does not affect the original value. At the same time, when the value of the reactive data object changes, the view is updated synchronously

ToRef:

As you can see, after +1 is applied to the value of the responsive data, the view does not change, the original value changes, and the value of the responsive data object also changes, indicating that toRef is a reference to the original and affects the original value, but does not update the view when the value of the responsive data object changes

Conclusion:

Ref is a copy of the incoming data; ToRef is a reference to the incoming data and changes in the value of ref will update the view; Changes in the value of toRef do not update the view

(6) toRefs

After toRef, it’s easy to understand toRefs. ToRefs converts the values of all properties in the passed object into reactive data objects. This function supports one parameter, the obj object

Let’s take a look at its basic use

Import {toRefs} from 'vue' export default {setup() {const obj = {name: 'front image ', age: 22, gender: 0 } // 2. Const state = toRefs(obj) const state = toRefs(obj) // 3. Log (state)}} </script>Copy the code

The print result is as follows:

The return is an object containing each of the wrapped reactive data objects

(7) shallowReactive

The API’s name, reactive, means that the original reactive is deep, and it’s used to optimize performance

In fact, when passing obj as a parameter to reactive data objects to generate reactive data objects, if there are more than one layer of OBj, each layer will be wrapped with Proxy. Let’s verify this

<script>
import {reactive} from 'vue'
export default {
    setup() {
        const obj = {
          a: 1,
          first: {
            b: 2,
            second: {
              c: 3
            }
          }
        }
        
        const state = reactive(obj)

        console.log(state)
        console.log(state.first)
        console.log(state.first.second)
    }
}
</script>
Copy the code

Take a look at the printout:

If you think about an object that has a deep hierarchy, then wrapping each layer in a Proxy can be very performance unfriendly

So let’s look at shallowReactive

<script> import {shallowReactive} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) console.log(state) console.log(state.first) console.log(state.first.second) }  } </script>Copy the code

Take a look at the printout:

The result is very clear, only the first layer is processed by the Proxy, that is, only if the value of the first layer is changed, the response will be updated, as follows:

<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button < span style =" box-sizing: border-box; color: RGB (255, 255, 255); line-height: 22px; font-size: 12px! Important; white-space: inherit! Important; 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowReactive(obj) function change1() { state.a = 7 } function change2() { state.first.b = 8 state.first.second.c = 9 console.log(state); } return {state} } } </script>Copy the code

Here’s how it works:

First, we click on the second button and change the values of b and C in the second and third layers, but the view is not updated.

When we click the first button and change layer A, the whole view is updated;

ShallowReactive listens for the value of the first layer property and updates the view if it changes

ShallowRef (8)

This is a shallow ref, just like shallowReactive, for performance optimization

ShallowReactive is listening for changes in the first layer of the object to drive view updates, and shallowRef is listening for changes in the value of. Value

Let’s look at the code

<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button </button> </template> <script> import {shallowRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); State. value = {a: 7, first: {b: 8, second: {c: 9 } } } } function change2() { state.value.first.b = 8 state.value.first.second.c = 9 console.log(state); } return {state, change1, change2} } } </script>Copy the code

What is the structure of the shallowRef wrapper

And then what happens when you change the value

We clicked the second button first and found that the data was changed, but the view was not updated;

The first button is clicked, reassigning the entire.value, and the view is updated immediately

We can use another API called triggerRef to update the view immediately. It takes a parameter state, which is the ref object that needs to be updated

So let’s use it

<template> <p>{{ state.a }}</p> <p>{{ state.first.b }}</p> <p>{{ state.first.second.c }}</p> <button </button> </template> <script> import {shallowRef, triggerRef} from 'vue' export default { setup() { const obj = { a: 1, first: { b: 2, second: { c: 3 } } } const state = shallowRef(obj) console.log(state); Function change () {state. The value. First. B = 8. The state value. The first, second, c = 9 / / immediately after the modification of value driver view update triggerRef (state) console.log(state); } return {state, change} } } </script>Copy the code

So let’s see how that works

As you can see, we didn’t re-assign.value, we just updated the view by calling triggerRef after we changed the value

(9) toRaw

The toRaw method is used to get the raw data from the ref or reactive object

Let’s start with a piece of code

< the template > < p > {{state. The name}} < / p > < p > {{state. The age}} < / p > < button @ click = "change" > change < / button > < / template > < script > Import {reactive} from 'vue' export default {setup() {const obj = {name: 'reactive ', age: 22 } const state = reactive(obj) function change() { state.age = 90 console.log(obj); Obj console.log(state); Return {state, change}}} </script>Copy the code

Let’s see how it works

When we change the data in the reactive object, we see that both the original data obj and the value of the object wrapped by reactive have changed. Therefore, we can see that the two are a reference relationship

So now we’re thinking, well, what happens if we just change the original obj? The answer is: the value of reactive will also change, but the view will not be updated

Therefore, when we want to modify the data, but do not want to update the view, we can choose to directly modify the value on the original data, so we need to get the original data first, we can use the toRaw method provided by Vue3

ToRaw takes a parameter, the ref object or reactive object

<script> import {reactive, toRaw} from 'vue' export default {setup() {const obj = {name: 'reactive ', age: 22 } const state = reactive(obj) const raw = toRaw(state) console.log(obj === raw) // true } } </script>Copy the code

The above code demonstrates that the toRaw method gets raw data from the reactive object, so it’s easy to do some performance optimization by changing the value of the raw data without updating the view

Note: In addition, when the toRaw method receives a ref object, it needs to add.value to get the original data object

MarkRaw (10)

The markRaw method can mark the original data as non-responsive, by wrapping it with ref or reactive, but still cannot achieve the data response, which takes a parameter, the original data, and returns the marked data

Let’s look at the code

< the template > < p > {{state. The name}} < / p > < p > {{state. The age}} < / p > < button @ click = "change" > change < / button > < / template > < script > Import {reactive, markRaw} from 'vue' export default {setup() {const obj = {name: 'reactive ', age: Const raw = markRaw(obj) // Try to package raw with reactive raw, reactive raw, reactive raw, reactive raw, reactive raw Const state = reactive(raw) function change() {state.age = 90 console.log(state); } return {state, change} } } </script>Copy the code

Reactive data (reactive data) reactive data (reactive data) reactive data (reactive dataAs you can see from the figure, the view will not be updated even if we change the value, that is, data responsive is not implemented

(11) provide && inject

Provide and inject are the same as those in Vue2, except that in Vue3 you need to import them manually from vue

Here’s a brief description of what these two methods do:

Provide: pass data to children and children. The first argument is key, which is the name of the data. The second parameter is value, which is the value of data inject: receive the data passed by the parent component or the ancestor component. Receive a parameter key, which is the name of the data passed by the parent or ancestor component. Suppose there are three components, a. ue, B. ue, and C. ue, where B. ue is a child of A. ue, and C. ue is a child of B. ue

// a.vue <script> import {provide} from 'vue' export default {setup() {const obj= {name: 'provide ', age: } provide('info', 'info', 'info', 'info'); Obj)}} </script> // b.vue <script> import {inject} from 'vue' export default {setup() {// receive data from a.vue Inject ('info') // {name: 'front impression ', age: 22}}} </script> // c.vue <script> import {inject} from 'vue' export default {setup() {// receive data from a.vue Inject (' info ') / / {name: 'front impression, age: 22}}} < / script >Copy the code

(12) Watch && watchEffect

Both Watch and watchEffect are used to monitor changes in data to perform specified operations, but there are differences in usage

Watch: watch(source, cb, [options])

Parameter description:

Cb: specifies the callback function that is executed when the dependent object changes. Options: Parameterable. The properties that can be configured include immediate (triggering the callback function immediately) and deep (deep listening).

<script> import {ref, watch} from 'vue' export default { setup() { const state = ref(0) watch(state, (newValue, OldValue) => {console.log(' original value ${oldValue} ') console.log(' newValue ${newValue} ') /* print the result after 1 second: SetTimeout (() => {state.value ++}, 1000)}} </script>Copy the code

When listening on reactive type:

<script> import {reactive, watch} from 'vue' export default { setup() { const state = reactive({count: 0}) watch(() => state.count, (newValue, OldValue) => {console.log(' original value ${oldValue} ') console.log(' newValue ${newValue} ') /* print the result after 1 second: SetTimeout (() => {state.count ++}, 1000)}} </script>Copy the code

When listening for multiple values:

<script> import {reactive, watch} from 'vue' export default { setup() { const state = reactive({ count: 0, name: 'zs' }) watch( [() => state.count, () => state.name], ([newCount, newName], [oldvCount, OldvName]) => {console.log(oldvCount) // old count value console.log(newCount) // newCount value console.log(oldName) // oldName Value console.log(newvName) // new name value}) setTimeout(() => {state.count ++ state.name = 'ls'}, 1000)}} </script>Copy the code

Since we already specify the object to listen to in the first argument of the watch method, we do not execute the callback in the second argument when the component is initialized. If we want to initialize it first, we can set immediate: true in the third argument object

By default, the watch method listens to the specified data layer by layer. For example, if there are multiple layers of nested data, deep data changes will not trigger the listening callback. If we want it to listen to deep data as well, we can set deep: true in the third argument object

Note: The watch method returns a stop method. If you want to stop the listener, you can simply execute the stop function

Next, let’s talk about watchEffect. The main differences between Watch and Watch are as follows:

We don’t need to pass in the dependency manually. We do a callback function to automatically retrieve the dependency each time we initialize. We can’t retrieve the original value, but only the changed value.

<script> import {reactive, watchEffect} from 'vue' export default { setup() { const state = reactive({ count: 0, name: 'zs'}) watchEffect(() => {console.log(state.count) console.log(state.name) /* Print when initialization: 0  1 ls */ }) setTimeout(() => { state.count ++ state.name = 'ls' }, 1000) } } </script>Copy the code

As you can see from the code above, instead of passing in a dependency like the watch method, we directly specify a callback function

When the component is initialized, the callback function is executed once and the data to be detected are state.count and state.name

Based on the above characteristics, we can choose which listener to use

GetCurrentInstance (13)

In Vue3, a lot of our code runs in the setup function, where this refers to undefined. So how do we get the instance of the current component?

You can use another method called getCurrentInstance

<template>
 <p>{{ num }}</p>
</template>
<script>
import {ref, getCurrentInstance} from 'vue'
export default {
    setup() { 
        const num = ref(3)
        const instance = getCurrentInstance()
        console.log(instance)

        return {num}
    }
}
</script>
Copy the code

Let’s take a look at the printoutBecause instance contains too much content, it is incomplete, but the main content is on the graph. Let’s focus on CTX and proxy, because these are the contents of this we wantYou can see that the contents of CTX and proxy are very similar, except that the latter wraps a layer of proxy relative to the former, which indicates that proxy is responsive

UseStore (14)

When we use Vuex in Vue2, we use this. Store to get the Vuex instance, but the previous part says that the original method of getting this in Vue2 is different. In addition, we also found no store in Vue3 getCurrentInstance().ctx to obtain Vuex instance, but the previous part said that the original method of obtaining this in Vue2 is different. In addition, we also found no store in Vue3 getCurrentInstance().ctx to obtain Vuex instance, but the previous part said that the original method of obtaining this in Vue2 is different. In addition, we did not find the store attribute in Vue3 getCurrentInstance().ctx, so how to get the Vuex instance? This is done through a method in VUex called useStore

Import Vuex from 'Vuex' const store = Vuex. CreateStore ({state: {name: 'front impression ', age: Mutations: {... },... }) // example.vue <script> import {useStore} from 'vuex' export default {setup() {// Get vuex Const store = useStore() console.log(store)}} </script>Copy the code

Let’s take a look at the printoutThen you can use vuex as normal as before

(15) Get the tag element

A final addition to the ref function is the ability to get tag elements or components

In Vue2, we get elements by giving them a ref attribute and then accessing them through this.$refs.xx, but this is no longer true in Vue3

How do you get elements in Vue3

<template> <div> <div ref="el">div element </div> </div> </template> <script> import {ref, OnMounted} from 'vue' export default {setup() {// create a DOM reference, OnMounted (() => {el.value.innerhtml = 'content modified'}) // Reference to the element created {el}}} </script>Copy the code

The operation of retrieving an element consists of the following steps:

Set a value for the target element’s ref attribute, let’s say el and then call the setup function ref null and assign it to the variable EL, and notice here, The variable name must be the same as the ref attribute name we set on the element. Return the element reference variable EL (return) to add: the element reference variable set is only accessible after the component is mounted, so any operation on the element before the mount is invalid

Of course, if we refer to a component element, then we get an instance object of that component, which I won’t go into too much of here

Third, conclusion

This article is also the author’s study and understanding of Vue3. Because in the process of learning before also refer to a large amount of document data, and constantly test, and its experience in Vue3 project, I had a deeper understanding of the Vue3, at the same time, I found that in every community or social group in a lot of friend of Vue3 apis are not very familiar with, even don’t know how these apis, So I’ve written this summary article to share what I know and understand