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