Vue3 study notes
I haven't seen the relevant knowledge of vue3 systematically before. I met the first Spring Festival holiday just more than a month after I joined the job, so I saw the relevant knowledge of vue3 during this period. Otherwise, the project is too hard to do
Vue3 has two ways to create a project
-
Scaffolding
-
Install via vite
What is vite A new generation of front-end building tools (similar to webpack)
Advantages: no packaging, quick start
Initialization project
Attention: the template structure in vue3 build can have no root tag
Common combined api
setup function
A new configuration item in Vue3, whose value is a function
The data and methods used in the component should be configured in setup
Finally, write the variables and functions in setup in return
<template> <h1>I am App assembly</h1> <h4>full name:{{name}}</h4> <h4>Age:{{age}}</h4> <button @click="sayHi">sayHi</button> </template> <script> export default { name: 'App', setup(){ let name = 'joy'; let age = 23; function sayHi() { console.log('Hi') }; return{ name, age, sayHi } }, } </script>
attention:
-
In Vue2 configuration (data, methods, computed), you can access the properties and methods in setup
-
However, Vue2 configuration (data, methods, computed) cannot be accessed in setup
-
If there are duplicate names, setup takes precedence
async cannot be used to decorate setup because the return value is a promise object and cannot be returned and accessed through return
ref function
ref handles simple types
ref function to convert a variable into a response
<template> <h1>Information registration</h1> <h4>full name:{{name}}</h4> <h4>Age:{{age}}</h4> <button @click="changeInfo">One click modification</button> </template> <script> import {ref} from 'vue' export default { name: 'App', setup(){ let name = ref('joy'); let age = ref(23); function changeInfo() { name.value = 'zoey' age.value = 18 // console.log(name,age) } return{ name, age, changeInfo } }, } </script>
Ref handles complex types
Basic type of data: the response type still depends on object get and set of defineproperty() are completed
Data of object type: internally, a new function in Vue3 - reactive function is used
<template> <h1>Information registration</h1> <h4>full name:{{name}}</h4> <h4>Age:{{age}}</h4> <h4>occupation:{{job.type}}</h4> <h4>Salary:{{job.salary}}</h4> <button @click="changeInfo">One click modification</button> </template> <script> import {ref} from 'vue' export default { name: 'App', setup(){ let name = ref('joy'); let age = ref(23); let job = ref({ type:'Front end development engineer', salary:'30K' }) function changeInfo() { job.value.type = 'Algorithm Engineer' job.value.salary = '50K' } return{ name, age, job, changeInfo } }, } </script>
reactive function
Handle the response data of object type, and do not use reactive for basic type
reactive defines responsive data as "deep-seated."
The internal Proxy implementation based on ES6 operates the internal data of the source object through the Proxy object
<template> <h1>Information registration</h1> <h4>full name:{{person.name}}</h4> <h4>Age:{{person.age}}</h4> <h4>occupation:{{person.job}}</h4> <h4>Salary:{{person.salary}}</h4> <button @click="changeInfo">One click modification</button> </template> <script> import {reactive} from 'vue' export default { name: 'App', setup(){ const person = reactive({ name:'joy', age : 23, job :{ title:'Front end development engineer', salary:'22.5K' } }) function changeInfo() { person.title = 'Algorithm Engineer', person.salary = '50K' } return{ person, changeInfo } }, } </script>
Responsive principle in Vue3
Vue2's responsive principle
Implementation principle:
-
Object type: through object Defineproperty() intercepts the reading and modification of properties (data hijacking)
-
Array type: intercept by rewriting a series of methods to update the array (wrap the change method of the array)
Object.defineProperty(data,'count',{ get(){}, set(){} })
Existing problems:
-
When adding or deleting attributes, the interface will not be updated
-
Modify the array directly through subscript, and the interface will not be updated automatically
Vue3's responsive principle
reactive vs ref
-
Compare from the perspective of defining data
ref is used to define basic type data
reactive is used to define: object (or array) type data
attention: ref can also be used to define object (or array) type data, which will be automatically converted into proxy object through reactive
-
Comparison from the perspective of principle
ref through object get and set of defineproperty () to implement responsive (data hijacking)
reactive implements responsive (data hijacking) by using Proxy, and operates the data inside the source object through Reflect
- From the perspective of use
ref defined data: required for operation data Value, which is not required for direct reading in the template when reading data value
reactive defined data: neither operation data nor read data: required value
Two points for attention in setup
- Timing of setup execution
Execute once before beforeCreate. this is undefined
- Parameters of setup
props: the value is an object, including the attributes passed from outside the component and declared and received inside the component
Context: context object
attrs: the value is an object, including: attributes passed from outside the component but not declared in the props configuration
emit: received slot content, equivalent to this$ slots
slots: a function that distributes custom events, equivalent to this$ emit
context
Demo component
<template> <h1>A person's information</h1> <h4>full name:{{person.name}}</h4> <h4>Age:{{person.age}}</h4> <button @click="test">Click me to test</button> </template> <script> import {reactive} from "vue"; export default { name: "Demo", props:{ name:{ type:String, required:true }, age:{ type:Number, required: true } }, // Registration event emits:['hello'], setup(props,context){ console.log('---context---',context) let person = reactive({ name:'joy', age:23 }) // methods function test(){ // Value transfer between components context.emit('hello',66) } return{ person, test } } } </script> <style scoped> </style>
App.vue
<template> <Demo name="zoey" age=18 @hello="helloMsg" ></Demo> </template> <script> import Demo from "./components/Demo.vue"; export default { name: 'App', components:{Demo}, setup(){ function helloMsg(value){ alert(`Hello, the parent component has received it Demo Value transfer of ${value}`) } return{ helloMsg } } } </script>
slots
Demo component
<template> <h1>A person's information</h1> <h4>full name:{{person.name}}</h4> <h4>Age:{{person.age}}</h4> <button @click="test">Click me to test</button> <hr> <slot name="god">I'm the default</slot> </template> <script> import {reactive} from "vue"; export default { name: "Demo", props:{ name:{ type:String, required:true }, age:{ type:Number, required: true } }, emits:['hello'], setup(props,context){ // props receive and transmit parameters // console.log('---props---',props) // emit triggers a custom event // console.log('---context---',context.emit) // Slot slot console.log('---context---',context.slots) let person = reactive({ name:'joy', age:23 }) // methods function test(){ context.emit('hello',66) } return{ person, test } } } </script> <style scoped> </style>
App.vue
<template> <Demo name="zoey" age=18 @hello="helloMsg"> <template v-slot:god> <span>Great God's Road</span> </template> </Demo> </template> <script> import Demo from "./components/Demo.vue"; export default { name: 'App', components:{Demo}, setup(){ function helloMsg(value){ alert(`Hello, the parent component has received it Demo Value transfer of ${value}`) } return{ helloMsg } } } </script>
Computed calculated properties
The integration becomes the computed() function
When using, it must be referenced first, and the callback function (ordinary function or arrow function) must be written in it
Calculation attribute abbreviation
Calculation attribute write all
<template> <h1>A person's information</h1> Last name:<input type="text" v-model="person.lastName"> <br> Name:<input type="text" v-model="person.firstName"> <br> full name:<span>{{person.fullName}}</span> </template> <script> import {reactive,computed} from "vue"; export default { name: "Demo", setup(){ let person = reactive({ firstName:'joy', lastName:'Zhang' }) // Calculation properties // Calculation properties - short form // person.fullName = computed(()=>{ // return person.firstName + person.lastName // }) person.fullName = computed({ get(){ return person.firstName +'-'+ person.lastName }, set(value){ const nameArr = value.split('-') person.firstName = nameArr[0] person.lastName = nameArr[1] } }) return{ person, } } } </script> <style scoped> </style>
watch property
situationI: monitor the responsive data defined by ref
Demo
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> </template> <script> import {ref,watch} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) //watch watch(sum,(newValue,oldValue)=>{ console.log('sum changed',newValue,oldValue) }) return{ sum } } } </script> <style scoped> </style>
situationII: watch monitors changes in multiple ref responsive data
Demo
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> <h2>Current information is{{msg}}</h2> <button @click="msg+='!'">click</button> </template> <script> import {ref,watch} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) let msg = ref('happy new year') watch([sum,msg],(newValue,oldValue)=>{ console.log('sum/msg changed',newValue,oldValue) },{immediate:true}) return{ sum, msg } } } </script> <style scoped> </style>
Situation III: watch listens to the data defined by reactive
-
Unable to get oldValue by listening to data defined by reactive using watch
-
In vue3, no matter how deep the nesting of objects is, as long as it is reactive data defined, use watch to enable deep listening
-
Depth listening is enabled by default and cannot be turned off through {deep:false}
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> <hr> <h2>Current information is{{msg}}</h2> <button @click="msg+='!'">click</button> <hr> <h2>full name:{{person.name}}</h2> <h2>Age:{{person.age}}</h2> <h2>occupation:{{person.job.job1.title}}</h2> <h2>salary:{{person.job.job1.salary}}K</h2> <button @click="person.name+='~'">Modify name</button> <button @click="person.age++">Growing age</button> <button @click="person.job.job1.salary++">Pay rise</button> </template> <script> import {ref,watch,reactive} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) let msg = ref('happy new year') let person = reactive({ name:'joy', age:23, job:{ job1:{ title:'Front end development engineer', salary:30 } } }) watch(person,(newValue,oldValue)=>{ console.log('person changed',newValue,oldValue) },{deep:true}) return{ sum, msg, person } } } </script> <style scoped> </style>
Situation IV: watch listens to an attribute in a responsive data defined by reactive
A property of the listening object must be written in the form of the return value of the arrow function, () = > {}
<script> watch(()=>person.job.job1.salary,(newValue,oldValue)=>{ console.log('person changed',newValue,oldValue) }) } } </script>
Situation V: watch monitors some attributes in a responsive data defined by reactive
<script> import {ref,watch,reactive} from "vue"; watch([()=>person.name,()=>person.age,()=>person.job.job1.salary],(newValue,oldValue)=>{ console.log('person changed',newValue,oldValue) }) </script>
exceptional case
When the monitored data is a deep attribute in an attribute in the reactive responsive data, the deep attribute needs to be added
Situations that cannot be monitored
watch(()=>person.job,(newValue,oldValue)=>{ console.log('job changed',newValue,oldValue) })
After adding deep, it can be monitored
watch(()=>person.job,(newValue,oldValue)=>{ console.log('job changed',newValue,oldValue) },{deep:true})
be careful:
-
When monitoring the reactive data defined by reactive, oldValue failed to get the data correctly and forced to enable deep listening (deep configuration failed)
-
The deep configuration is valid when monitoring an object attribute in the reactive data defined by reactive
watchEffect function
-
watch: indicate both the monitored attribute and the monitored callback
-
watchEffect: it is not necessary to specify which attribute to monitor, and which attribute is used in the monitored callback, so which attribute to monitor
-
watchEffect is a bit like computed:
computed focuses on the calculated value (the return value of the callback function), so the return value must be written
watchEffect pays more attention to the process (the function body of the callback function), so there is no need to write the return value
watchEffect(()=>{ // As long as the value is used, automatic monitoring will be enabled const x1 = sum.value const x2 = person.job.job1.salary console.log('watchEffect Triggered') })
Life cycle of Vue3
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeUnmount
unmounted
Demo component
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> </template> <script> import {ref,watchEffect,reactive} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) return{ sum, } }, beforeCreate() { console.log('---beforeCreate---') }, created() { console.log('---created---') }, beforeMount() { console.log('---beforeMount---') }, mounted() { console.log('---mounted---') }, beforeUpdate() { console.log('---beforeUpdate---') }, updated() { console.log('---updated---') }, beforeUnmount() { console.log('---beforeUnmount---') }, unmounted() { console.log('---unmounted---') } } </script> <style scoped> </style>
App.vue
<template> <Demo v-if="isShowDemo"></Demo> <br> <br> <button @click="isShowDemo = !isShowDemo">Click display/hide</button> </template> <script> import {ref} from 'vue' import Demo from "./components/Demo.vue"; export default { name: 'App', components:{Demo}, setup(){ let isShowDemo = ref(true) return{ isShowDemo } } } </script>
Life cycle hook function of composite api
Vue3 also provides a life cycle hook in the form of composite api. The corresponding relationship with the hook in Vue2 is as follows:
Note: the way of composite api is to integrate all these life cycle hook functions into setup()
beforeCreated ====> setup()
created ====> setup()
beforeMount =====> onBeforeMount
mounted ====> onMounted
beforeUpdate ====> onBeforeUpdate
updated ====> onUpdated
beforeUnmount ====> onBeforeUnmount
unmounted ====> onUnmounted
Demo component
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> </template> <script> import {ref,watchEffect,reactive,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) console.log('---setup---') onBeforeMount(()=>{ console.log('---onBeforeMount---') }) onMounted(()=>{ console.log('---onMounted---') }) onBeforeUpdate(()=>{ console.log('---onBeforeUpdate---') }) onUpdated(()=>{ console.log('---onUpdated---') }) onBeforeUnmount(()=>{ console.log('---onBeforeUnmount---') }) onUnmounted(()=>{ console.log('---onUnmounted---') }) return{sum,} }, // beforeCreate() { // console.log('---beforeCreate---') // }, // created() { // console.log('---created---') // }, // beforeMount() { // console.log('---beforeMount---') // }, // mounted() { // console.log('---mounted---') // }, // beforeUpdate() { // console.log('---beforeUpdate---') // }, // updated() { // console.log('---updated---') // }, // beforeUnmount() { // console.log('---beforeUnmount---') // }, // unmounted() { // console.log('---unmounted---') // } } </script> <style scoped> </style>
attention:
Using the hook function in the composite api, the trigger is faster than the configuration item
Custom hook function
-
What is hook—— It is essentially a function that encapsulates the combinatorial api used in the setup function
-
Similar to mixin in vue2
-
Advantages of custom hook: reuse code to make the logic in setup clearer and easier to understand
Case: click the mouse to obtain the coordinates clicked by the mouse
Demo component
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> <hr> <h2>The current coordinates are x:{{position.x}} y:{{position.y}}</h2> </template> <script> import {ref, reactive, onMounted, onBeforeUnmount} from "vue"; export default { name: "Demo", setup(){ let sum = ref(0) let position = reactive({ x:0, y:0 }) const getPosition = (e)=>{ position.x = e.pageX position.y =e.pageY console.log(e.pageX,e.pageY) } onMounted(()=>{ window.addEventListener('click',getPosition) }) onBeforeUnmount(()=>{ window.removeEventListener('click',getPosition) }) return{sum,position} }, } </script> <style scoped> </style>
Using hooks
Create a hooks folder in the src directory, which defines a js file
import {ref, reactive, onMounted, onBeforeUnmount} from "vue"; export function usePosition() { let position = reactive({ x:0, y:0 }) const getPosition = (e)=>{ position.x = e.pageX position.y =e.pageY console.log(e.pageX,e.pageY) } onMounted(()=>{ window.addEventListener('click',getPosition) }) onBeforeUnmount(()=>{ window.removeEventListener('click',getPosition) }) //There must be a return value here return position }
In the component, reference hooks, and then use the value to receive the value passed by the function
<template> <h2>The current summation is:{{sum}}</h2> <button @click="sum++">Point me+1</button> <hr> <h2>The current coordinates are x:{{position.x}} y:{{position.y}}</h2> </template> <script> import {ref, reactive, onMounted, onBeforeUnmount} from "vue"; import {usePosition} from "../hooks/usePosition"; export default { name: "Demo", setup(){ let sum = ref(0) const position = usePosition() return{sum,position} }, } </script> <style scoped> </style>
toRef
Change an attribute in an object into responsive data
Demo component
<template> <h2>full name:{{person.name}}</h2> <h2>Age:{{person.age}}</h2> <h2>occupation:{{person.job.job1.title}}</h2> <h2>salary:{{person.job.job1.salary}}K</h2> <button @click="person.name+='~'">Modify name</button> <br> <br> <button @click="person.age++">Growing age</button> <br> <br> <button @click="person.job.job1.salary++">Pay rise</button> <br> <br> </template> <script> import {ref,toRef,reactive} from "vue"; export default { name: "Demo", setup(){ let person = reactive({ name:'joy', age:23, job:{ job1:{ title:'Front end development engineer', salary:30 } } }) return{ person, name:toRef(person,'name'), age:toRef(person,'age'), title: toRef(person.job.job1,'title'), salary: toRef(person.job.job1,'salary') } } } </script> <style scoped> </style>
toRefs
Turn all attributes in an object into responsive data
<template> <h2>full name:{{person.name}}</h2> <h2>Age:{{person.age}}</h2> <h2>occupation:{{person.job.job1.title}}</h2> <h2>salary:{{person.job.job1.salary}}K</h2> <button @click="person.name+='~'">Modify name</button> <br> <br> <button @click="person.age++">Growing age</button> <br> <br> <button @click="person.job.job1.salary++">Pay rise</button> <br> <br> </template> <script> import {ref,toRef,reactive,toRefs} from "vue"; export default { name: "Demo", setup(){ let person = reactive({ name:'joy', age:23, job:{ job1:{ title:'Front end development engineer', salary:30 } } }) const x = toRefs(person) console.log('x=======',x) return{ person, name:toRef(person,'name'), age:toRef(person,'age'), title: toRef(person.job.job1,'title'), salary: toRef(person.job.job1,'salary') } } } </script> <style scoped> </style>
Other composite APIs
shallowReactive and shallowRef
readonly and shallowReadonly
toRaw and markRaw
customRef
provide and inject
Judgment of responsive data
isRef: check whether a value is a ref object
isReactive: checks whether an object is a responsive proxy created by reactive
isReadonly: checks whether an object is a read-only proxy created by readonly
isProxy: check whether an object is a proxy created by the reactive or readonly method
<template> <Demo v-if="isShowDemo"></Demo> <br> <br> <button @click="isShowDemo = !isShowDemo">Click display/hide</button> </template> <script> import {ref,reactive,readonly,isRef,isReactive,isReadonly,isProxy} from 'vue' import Demo from "./components/Demo.vue"; export default { name: 'App', components:{Demo}, setup(){ let isShowDemo = ref(true) let car = reactive({name:'nio',price:'40w'}) let carCopy = readonly(car) console.log(isRef(isShowDemo)) //true console.log(isReactive(car)) //true console.log(isProxy(car)) //true console.log(isReadonly(carCopy)) //true console.log(isProxy(carCopy)) //true //readonly is set to read-only and does not change the Proxy attribute of the car return{isShowDemo,car,carCopy} } } </script>
Advantages of Composition api
Problems with the option api
In the traditional option API, to add or modify a requirement, you need to modify it in data, methods and calculated respectively
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-3bnbfkgr-164421795480)( file:///Users/joy.zhang1/Library/Application%20Support/marktext/images/2022 -02-02-11-20-47-image. png)]
Advantages of Composition API
We can organize our code and functions more gracefully. Make the code more organized and relevant
[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-fl9kdcq7-164421795481)( file:///Users/joy.zhang1/Library/Application%20Support/marktext/images/2022 -02-02-11-19-48-image. png)]
New components
Fragment
-
In vue2: the component must have a root label
-
In Vue3, components can have no root tag, and multiple tags will be included in a Fragment virtual element
-
Benefits: reduce tag levels and memory usage
Teleport
Suspense
It is still in the experimental stage