Lifecycle hook
We can look directly at the lifecycle diagram to see what kind of lifecycle hooks there are:
The full lifecycle hooks are shown below:
We can see that beforeCreate and Created are replaced by setup. Second, hook names add on; Vue3. X also adds hook functions onRenderTriggered and onrendertrickeda for debugging
Vue3. X has hooks that need to be imported from vue:
<template> <div>{{num}}</div> </template> <script> import { ref, defineComponent, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured, onRenderTracked, onRenderTriggered } from "vue"; Export default defineComponent({// beforeCreate and created are vue2 beforeCreate() {console.log("------beforeCreate-----"); }, created() { console.log("------created-----"); }, setup() { console.log("------setup-----"); / / vue3. Write x life cycle in the setup onBeforeMount (() = > {the console. The log (" -- -- -- -- -- - the onBeforeMount -- -- -- -- -- "); }); onMounted(() => { console.log("------onMounted-----"); }); onUpdated(() => { console.log('updated! ') }) onUnmounted(() => { console.log('unmounted! ') }) const num = ref(0) setInterval(() => { num.value++; OnRenderTriggered ((event) => {console.log("------onRenderTriggered-----", event); }) return { num } }, }); </script>Copy the code
OnUpdated and onRenderTriggered are triggered when setInterval is used to change data.
setup
Execution order
export default defineComponent ({
beforeCreate() {
console.log("----beforeCreate----");
},
created() {
console.log("----created----");
},
setup() {
console.log("----setup----"); },}) setup beforeCreate createdCopy the code
Warning Due to executionsetup
The component instance has not been created atsetup
None of the optionsthis
.
The setup parameters
Setup takes two arguments:
- Props: Properties/parameters passed by the component
- context
The props accepted in setup is reactive, and because it is reactive, you cannot use ES6 deconstruction, because deconstruction eliminates its reactive.
Example of incorrect code for this rule that causes props to no longer support reactive:
export default defineComponent ({
setup(props, context) {
const { name } = props
console.log(name)
},
})
Copy the code
If you want to use structures, you need to use the official toRefs, which we’ll cover later.
Setup The second argument, context, does not have access to Vue2’s most commonly used this object, so context provides the three most commonly used properties of this: Attrs, Slot, and emit correspond to the $attr attribute, slot slot, and $emit emit event in vue2. x respectively. These attributes are automatically synchronized to the latest value, so we get the latest value every time we use them.
Reactive, REF and toRefs
In VUe2. X, bidirectional binding is defined in data, but Vue3 uses Reactive and REF to define bidirectional binding data.
So what’s the difference between ref and Reactive?
<template> <div>{{obj.name}}-{{obj.count}}</div> <div>{{basetype}}</div> <div>{{baseTypeReactive}}</div> <div>{{objreactive.count}}</div> </template> <script> import { reactive, ref } from 'vue'; export default { setup() { const obj = ref({ count: 1, name: }) const baseType = ref(2) setTimeout(() => {obj.value.count = obj.value.count + 1 obj.value.name = "" basetype.value += 1 }, 1000) const baseTypeReactive = reactive(6) const objreactive = reactive({ count: 10 }) return { obj, basetype, baseTypeReactive, objreactive } }, } </script>Copy the code
reactive
reactive
Is a method for implementing responsive data provided in Vue3.- Reactive data is implemented via defineProperty in Vue2 and ES6 in Vue3
Proxy
To achieve. - The reactive parameter must be an object (JSON/ARR) and cannot represent basic types such as strings, numbers, and Boolean.
- Essence: Wraps the incoming data into a Proxy object
- If you pass other objects (such as Date objects) to Reactive
- By default, the modified object does not enable data binding updates to the interface.
- If updates are required, reassignment is required. (That is, it is not allowed to manipulate the data directly, and a new data needs to be put in place of the original data)
inreactive
Use basic type parameters
Basic types (numbers, strings, booleans) cannot be created as proxy objects in Reactive, so they cannot be listened to and responsive.
<template>
<div>
<p>{{msg}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive(0)
function c() {
console.log(msg);
msg++;
}
return {
msg,
c
};
}
}
</script>
Copy the code
When we click on the button, we expect the number to change from 0 to 1, but the actual number on the interface does not change at all.
Looking at the console, its output looks like this (I clicked it three times)
prompted
value cannot be made reactive: 0
The output does change, but the change is not fed back to the interface, meaning there is no two-way data binding. Of course, if it is ref, there is no such problem. To use Reactive, we need to convert parameters from primitive types to objects.
<template>
<div>
<p>{{msg.num}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive({
num: 0
})
function c() {
console.log(msg);
msg.num++;
}
return {
msg,
c
};
}
}
</script>
Copy the code
The parameter is replaced with the object {num: 0}, at which point the interface changes by clicking the button (I clicked it three times).
Prints the message at the console
It can be seen that MSG is successfully created as a proxy object, and it realizes bidirectional data binding of the object by hijacking the get and set methods of the object.
Deep, internal object changes can also be detected (note the inner in the code below)
<template>
<div>
<p>{{msg.num.inner}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive({
num: {
inner: 0
}
})
function c() {
console.log(msg);
msg.num.inner ++;
}
return {
msg,
c
};
}
}
</script>
Copy the code
Array changes can also be listened for, of course
<template>
<div>
<p>{{msg}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive([1, 2, 3])
function c() {
console.log(msg);
msg[0] += 1;
msg[1] = 5;
}
return {
msg,
c
};
}
}
</script>
Copy the code
Object arrays are also available
<template>
<div>
<p>{{msg}}</p>
<button @click="push">push</button>
<button @click="change">change</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive([{
name: 'lilei',
age: 12
}])
function change() {
console.log(msg);
msg[0].age += 1;
}
function push() {
console.log(msg);
msg.push({
name: 'zhaodafa',
age: 22
})
}
return {
msg,
change,
push
};
}
}
</script>
Copy the code
Special circumstances:reactive
In the listeningDate
Date format data
If the argument is not an array or an object, but a slightly odder data type, such as Date, then the problem is again.
<template>
<div>
<p>{{msg}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive(new Date())
function c() {
console.log(msg);
msg.setDate(msg.getDate() + 1);
console.log(msg);
}
return {
msg,
c
};
}
}
</script>
Copy the code
Here, I printed MSG twice first. It can be seen that when I clicked button once, the data of MSG was changed, but the interface was not changed. Meanwhile, we found that MSG was not identified as a proxy in the console.
Even if we put Date in the object, as follows:
<template>
<div>
<p>{{msg.date}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive({
date: new Date()
});
function c() {
console.log(msg);
msg.date.setDate(msg.date.getDate() + 1);
console.log(msg);
}
return {
msg,
c
};
}
}
</script>
Copy the code
It still doesn’t work.
Obviously, we need to do something special for this data type.
This is done by reassigning (rather than modifying the original value directly).
<template>
<div>
<p>{{msg.date}}</p>
<button @click="c">button</button>
</div>
</template>
<script>
import {
reactive
} from 'vue'
export default {
name: 'App',
setup() {
let msg = reactive({
date: new Date()
});
function c() {
console.log(msg);
msg.date.setDate((msg.date.getDate() + 1));
msg.date = new Date(msg.date);
console.log(msg);
}
return {
msg,
c
};
}
}
</script>
Copy the code
Here I used the copy scheme to reassign MSG. Date, and the interface successfully changed (date + 1).
ref
Ref can listen on complex objects as well as underlying data types, as follows:
<template> <! -- Take the value directly, No XXX. Value -- -- > < div > {{obj. Name}} - {{obj. Count}} < / div > < div > {{basetype}} < / div > < div > {{date}} < / div > < / template > < script > import { ref } from 'vue'; export default { setup() { const obj = ref({ count: 1, name: }) const baseType = ref(2) const date = ref(new date ()) setTimeout(() => {obj.value.count = obj.value.count + 1 Value += 1 date.value.setDate((date.value.getDate() + 1))); // date.setDate((date.value.getDate() + 1))); // date = new Date(date); }, 1000) return { obj, basetype, date } }, } </script>Copy the code
Ref listens for the Date type and can change the Date type directly without copying the assignment
Note, however, that the ref listens on objects that need to be assigned and evaluated using xxx.value in the setup method; The value can be directly evaluated on the page
Deconstruction method: toRefs
The answer is that you can’t structure the user directly, which would eliminate its responsiveness, as we’ve already mentioned. Above we said that props cannot be deconstructed directly using ES6
The solution is toRefs.
ToRefs is used to convert a Reactive object to a normal object whose properties are all REF objects. The specific usage is as follows:
< the template > < div class = "homePage" > < p > the first {{year}}, < / p > < p > name: {{nickname}} < / p > < p > age: {{ age }}</p> </div> </template> <script> import { defineComponent, reactive, ref, toRefs } from "vue"; export default defineComponent({ setup() { const year = ref(0); Reactive ({nickname: "xiaofan", age: 26, gender: "female"}); SetInterval (() => {year.value++ user.age++}, 1000) return {year, // reRefs... toRefs(user) } }, }); </script>Copy the code
Watch and watchEffect
The watch function listens for a specific data source and performs side effects in the callback function. The default is lazy, meaning that callbacks are executed only when the source data being listened on changes.
watch(source, callback, [options])
Copy the code
Parameter Description:
- Source: can support string, Object, Function, Array; Used to specify the reactive variable to listen for
- Callback: Callback function to execute
- Options: Supports deep, immediate, and Flush options.
Listen to data defined by Reactive
<template> <div>{{nickname}}</div> </template> <script> import { defineComponent, ref, reactive, toRefs, watch } from "vue"; export default defineComponent({ setup() { const state = reactive({ nickname: "xiaofan", age: 20 }); SetTimeout (() => {state.age++}, 1000) watch(() => state.age, (curAge, PreAge) = > {the console. The log (" new values: "curAge," old value: ", preAge); }); return { ... toRefs(state) } }, }); </script>Copy the code
Listen for data defined by ref
Const year = ref(0) setTimeout(() =>{year.value ++},1000) watch(year, (newVal, oldVal) =>{console.log("新值:", newVal, ", oldVal); })Copy the code
Listening for multiple data
In the above two examples, we use two Watches respectively. When we need to listen to multiple data sources, we can merge and listen to multiple data simultaneously:
Watch ([() = > state. The age, year], [[curAge preAge], [newVal, oldVal]) = > {the console. The log (" new values: "curAge," old value: ", preAge); Console. log(" new value :", newVal, "old value :", oldVal); });Copy the code
Listen for complex nested objects
In our actual development, we see complex data everywhere, such as:
Const state = reactive({room: {id: 100, attrs: {size: "140 m2 ", type:" 140 m2 "},},}); Watch (() => state.room, (newType, oldType) => {console.log(" new value :", newType, "old value :", oldType); }, {deep:true});Copy the code
In complex data access, if the third parameter is not useddeep:true
, is unable to listen to data changes.
As we mentioned earlier, watch is lazy by default, so when is it not lazy and the callback can be executed immediately? Set the third parameter to immediate: true.
Stop Stop listening
The Watch listener we created in the component will stop automatically when the component is destroyed. If we want to stop a listener before the component is destroyed, we can call the return value of the watch() function as follows:
Const stopWatchRoom = watch(() => state.room, (newType, oldType) => {console.log(" new value :", newType, "old value :", oldType); }, {deep:true}); SetTimeout (()=>{// stop listening stopWatchRoom()}, 3000)Copy the code
There is also a watchEffect function. In my opinion, Watch can meet the needs of monitoring, so why there is a watchEffect function? Although I didn’t get the need for it, I still want to introduce the watchEffect, first of all, to see how it is different from Watch.
import { defineComponent, ref, reactive, toRefs, watchEffect } from "vue";
export default defineComponent({
setup() {
const state = reactive({ nickname: "xiaofan", age: 20 });
let year = ref(0)
setInterval(() =>{
state.age++
year.value++
},1000)
watchEffect(() => {
console.log(state);
console.log(year);
}
);
return {
...toRefs(state)
}
},
});
Copy the code
The state and year values are printed first. Then, every second, print the state and year values.
As you can see from the code above, unlike Watch, dependencies are not passed in first. WatchEffect collects them automatically by specifying a callback function. At component initialization, dependencies are collected once, and then callbacks are executed again when the data in the collected dependencies changes.
So the comparison is as follows:
- WatchEffect does not require manually passing in dependencies
- WatchEffect is executed once to automatically collect dependencies
- WatchEffect does not get the pre-change value, only the post-change value
Tips: If you define a non-responsive value, watch and watchEffect will not be able to listen for the value change!!
Custom Hooks
In VUe2 mixins can be extracted to implement common logic functions (but their drawbacks won’t be discussed here). In VUe3 mixins can be encapsulated as hooks, and we agree that these “custom hooks” are prefixed with use to distinguish them from normal functions.
UseCount. Js implementation:
import {
ref,
computed
} from "vue";
export default function useCount(initValue = 1) {
const count = ref(initValue);
const increase = (delta) => {
if (typeof delta !== "undefined") {
count.value += delta;
} else {
count.value += 1;
}
};
const multiple = computed(() => count.value * 2)
const decrease = (delta) => {
if (typeof delta !== "undefined") {
count.value -= delta;
} else {
count.value -= 1;
}
};
return {
count,
multiple,
increase,
decrease,
};
}
Copy the code
Let’s look at using the useCount hook in a component:
<template> <div> <p>count: {{count}}</p> <p> {{multiple}} < / p > < div > < button @ click = "happens ()" > 1 < / button > < button @ click = "decrease ()" > minus one < / button > < / div > < / div > </template> <script> import { defineComponent } from 'vue'; import useCount from ".. /hooks/useCount"; export default defineComponent({ setup() { const { count, multiple, increase, decrease } = useCount(10); return { count, multiple, increase, decrease, }; }}); </script>Copy the code
Parameter was transmitted to the props parent and child components
Parent and child components pass parameters, such as the name in
/ / the parent component
<template>
<div>The parent component basetype: {{basetype}}<button @click="addNum">addNum</button>
</div>
<test :name="basetype"></test>
</template>
<script>
import {
ref, defineComponent, getCurrentInstance, readonly
} from 'vue';
import test from './test.vue'
export default defineComponent({
components: { test },
setup() {
const basetype = ref(2)
function addNum() {
basetype.value += 1
}
return {
addNum,
basetype,
}
},
})
</script>
Copy the code
/ / child component
<template>
<div style="border: 1px solid red;">
<h2>Subcomponent name from baseType :{{name}}</h2>
</div>
</template>
<script>
import { defineComponent, watch } from 'vue'
export default defineComponent({
props: {
name: {
type: Number}},setup(props) {
watch(props, (val, oldVal) = > {
console.log('Subcomponent Watch:' + JSON.stringify(val))
})
return {
// The value passed by props is used directly in the template without a return
// name: props. Name // This cannot be written; Return {name: 2}; It's not going to be reactive}}})</script>
Copy the code
Teleport
Teleport is A way of binding component locations to defined Dom locations, like doraemon’s “any door”. Previously, Element UI dialog and select components are not bound to the parent component’s Dom Element by default, but to the global Element under the body.
Our Teleport is similar to this function, but it can be tied wherever you want. Let’s take a look at a small example:
<template> <div> parent </div> < button@click ="show =! Show ">changeShow</button> <modal V-if ="show"> <div> Subcomponent slot</div> <div slot="footer"> Subcomponent footer slot</div> </modal> </template> <script setup> import { ref, defineComponent } from 'vue'; import modal from './test.vue' let show = ref(false) </script>Copy the code
// Teleport component <template> <teleport to="body"> // This determines the binding element to the body <div :class="$style.modal"> Modal <div class="modal_content"> <slot></slot> </div> <div class="modal_footer"> <slot name="footer"></slot> </div> </div> </teleport> </template> <style lang='scss' module> .modal { border: 1px solid red; } </style>Copy the code
Change the body to
. You can see that the binding position has changed
Suspense
Suspense is a new built-in tag in Vue 3. It is aimed at demoted rendering effect of asynchronous components. It can handle rendering Loading of components when the request data is not returned.
Asynchronous components typically have the following situations:
- Display loading animation before the page loads
- Displays placeholder content
- Handles lazy loading of images and files
- Skeleton screen
In Vue 2 we used to have to use v-if with a loading flag bit to check that our data was loading and displaying content, but now we can use Suspense with Vue3. Here’s the code:
<template> <Suspense> <template #default> <asyncCom></asyncCom> </template> <template #fallback> <div>Loading... </div> </template> </Suspense> </template> <script setup> import { ref, defineAsyncComponent } from 'vue'; const asyncCom = defineAsyncComponent(() => import("./test.vue")); </script>Copy the code
<template> <div> <ul> <li v-for="item in jsonData" :key="item.name">{{ item.name }} - {{ item.age }}</li> </ul> </div> </template> <script setup> import { ref } from "vue"; Function fetchData() {return new Promise((resolve) => {setTimeout(() => {resolve([{name: "", age: 15,}, {name: "Li Si ", age: 17},]); }, 1500); }); } ref: jsonData = await fetchData(); </script>Copy the code
The effect is as follows:
Before loading asynchronous components:
After loading the asynchronous component:
Vue executes all statements in the child’s setup from top to bottom. After the synchronization statement (including the await statement) is executed, the parent considers the child to be loaded. The child setup state is not pending until then, so the parent displays the Loading content. When the child setup state changes to Resolved or Rejected, the parent component displays the default.
Read more:
Object.defineProperty vs Proxy
When Vue2. X, we often encountered a problem, data update ah, why the page is not updated? When to update with $set, when to force updates with $forceUpdate, and if you were in trouble at one point. Later in the learning process, I started to contact the source code and realized that the root of everything was Object.defineProperty.
Here’s a quick comparison between Object.defineProperty and Proxy
Object.defineProperty
You can only hijack the properties of an object, whereas a Proxy directly proxies an object becauseObject.defineProperty
You can only hijack the attributes of an object. You need to traverse every attribute of the object, and if the value of the attribute is also an object, you need to recursively perform deep traversal. However, Proxy directly proxies objects without traversalObject.defineProperty
New attributes need to be manually addedObserve
Because theObject.defineProperty
The object attributes are hijacked, so when adding attributes, you need to iterate over the object and use the new attributes againObject.defineProperty
To hijack. Vue2. X is used to add new attributes to arrays and objects$set
To make sure that the new properties are also responsive,$set
Internally also by callObject.defineProperty
To deal with it.
Refer to blog post:
Reactive attention point in [1] vue3 series (4) www.cnblogs.com/fsg6/p/1448…
[2] the Vue 3 newly introduced Suspense component is introduced www.jianshu.com/p/4bc2dfba1…