This is a summary of my personal notes on the Vue3 Composition API.
setup
Setup is a configuration item for a component, and the value is a function. The data, methods, computed properties, etc. used in the component are configured in the Setup.
The timing of setup is to execute once before beforeCreate. The this value is undefined.
// App.vue
<template>
<h1>App</h1>
</template>
<script>
export default {
name: 'App'.beforeCreate() {
console.log('beforeCreate')},created() {
console.log(this) // undefined
console.log('created')},setup() {
console.log('setup')}}</script>
// setup
// beforeCreate
// created
Copy the code
The setup function takes props and context.
Props parameters
Props is a property declared internally by a component. Props are responsive and cannot be deconstructed using ES6. Here’s an example:
// Parent component: app.vue
<template>
<child name="Zhang" :age="20" />
</template>
<script>
import Child from './components/Child'
export default {
name: 'App'.components: { Child }
}
</script>
Copy the code
// Child component: /components/ child.vue
<template>
<p>The Child Child components</p>
</template>
<script>
export default {
props: ['name'.'age'.'sex'].name: 'Child'.setup(props, context) {
console.log(props) // Proxy {name: undefined, age: 20, sex: undefined}}}</script>
Copy the code
The context parameter
Context is a normal JavaScript object that exposes three properties of a component:
attrs
: passed from outside the component, but not inprops
Properties declared in the configuration.slots
: Slot content received. The default name is thedefault
.emit
: Function that distributes custom events.
// The parent component app.vue
<template>
<child name="Zhang" :age="20" @customClick="customClick">
<template>
<p>It's called the default slot</p>
</template>
<template #footer>
<p>Named footer slot</p>
</template>
</child>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App'.components: { Child },
setup() {
function customClick(num) {
console.log(Trigger customClick '- >', num) // Trigger customClick--> 666
}
return {
customClick
}
}
}
</script>
Copy the code
// Child component: /components/ child.vue
<template>
<p>The Child Child components</p>
</template>
<script>
export default {
props: ['name'].name: 'Child'.setup(props, context) {
console.log(context.attrs) // Proxy {age: 20, onCustomClick: ƒ}
console.log(context.slots) // Proxy {footer: logon, default: logon}
context.emit('customClick'.Awesome!) // Triggers custom events}}</script>
Copy the code
ref
Ref defines reactive data and returns an object containing reactive data. It is generally used to define a basic data type, but reference types can also be defined.
Ref if passed the base data type is still done based on the get and set of object.defineProperty (). If you pass in a reference type, you are calling the reactive function internally, which implements the object based on the Proxy.
For data wrapped by ref, the return value is an object that needs to be obtained through.value.
// App.vue<template> <! It is not required in the template.<p>{{ msg }}</p>
<p>{{ person.name }}</p>
<button @click="handleClick">Modify the</button>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App'.setup() {
// Basic data types
const msg = ref('A message')
// Reference type
const person = ref({ name: 'Joe'.age: 20 })
// Use.value to modify data
function handleClick() {
msg.value = 'New message'
person.value.name = 'bill'
}
return {
msg,
person,
handleClick
}
}
}
</script>
Copy the code
shallowRef
ShallowRef only handles responses of basic types, not references.
If there is an object in which subsequent functions do not modify the properties of that object, but generate new objects to replace them:
<template>
<p>{{ msg }}</p>
<p>{{ person.age }}</p><! -- MSG (MSG) --><input v-model="msg" /><! -- Object not listening --><input v-model.number="person.age" />
<button @click="handleClick">Change it to a new object</button>
</template>
<script>
import { shallowRef } from 'vue'
export default {
name: 'App'.setup() {
const msg = shallowRef('A message')
let person = shallowRef({
age: 20
})
function handleClick() {
// Generate new objects
person.value = {
age: 30}}return {
msg,
person,
handleClick
}
}
}
</script>
Copy the code
reactive
Reactive function provides the Proxy implementation of ES6.
Define reactive functions for an object type (don’t use reactive functions for basic data types). Returns a Proxy object for a Proxy. Reactive data is deep.
// App.vue
<template>
<p>{{ person.name }}</p>
<p>{{ person.job.jobName }}</p>
<button @click="handleClick">Modify the</button>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})function handleClick() {
person.name = 'bill'
person.job.jobName = 'Designer'
}
return {
person,
handleClick
}
}
}
</script>
Copy the code
shallowReactive
ShallowReactive only considers the first level of the object type.
If there is an object with a deep data structure, but only the first layer properties change:
<template>
<p>{{ person.name }}</p>
<p>{{ person.job.jobName }}</p>
<input v-model="person.name" /><! -- inner attribute cannot be heard --><input v-model="person.job.jobName" />
</template>
<script>
import { shallowReactive } from 'vue'
export default {
name: 'App'.setup() {
const person = shallowReactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})return {
person
}
}
}
</script>
Copy the code
computed
There are two methods for writing computed:
-
Accepts a shortened form of the getter function.
-
How objects receive get and set functions.
getter
shorthand
<template>
<p>{{ person.fullName }}</p>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
firstName: '张'.lastName: '三'
})
// Can be put directly into the object
person.fullName = computed(() = > {
return `${person.firstName}-${person.lastName}`
})
return {
person
}
}
}
</script>
Copy the code
get
andset
way
<template>
<input type="text" v-model="person.fullName" />
</template>
<script>
import { reactive, computed } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
firstName: '张'.lastName: '三'
})
person.fullName = computed({
get() {
return `${person.firstName}-${person.lastName}`
},
set(newVal) {
console.log(newVal)
}
})
return {
person
}
}
}
</script>
Copy the code
watch
The Vue3watch has the same function as the Vue2 Watch. But there are still some small details to be paid attention to. ⚠ ️
Watch takes three arguments:
params
: a reactive property orgetter
Function.handler
: callback function.object
: Is optional.
Watch (params, handler(newValue, oldValue), {immediate: true.deep: true })
Copy the code
Listen for responsive data defined by the REF
<template>
App
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App'.setup() {
const msg = ref('A message')
const person = ref({
name: 'Joe'.age: 20
})
// Listen for basic data types defined by ref
watch(msg, (newValue, oldValue) = > {
console.log(NewValue ` MSG:${newValue}OldValue of MSG:${oldValue}`)})// Listen for reference types defined by ref, so use {deep: true}. Otherwise you can't listen
watch(
person,
(newValue, oldValue) = > {
// The name is the same as the name.
console.log(The newValue ` person. Name:${newValue.name}, person oldValue. Name:${oldValue.name}`)}, {deep: true }
)
msg.value = 'New message' // newValue of MSG: new message, oldValue of MSG: one message
person.value.name = 'bill' // person newvalue. name: li si, person oldValue. Name: Li si
return { msg, person }
}
}
</script>
Copy the code
When the reference type defined by ref is used for listening, {deep: true} must be configured. Otherwise, the listening cannot be performed.
Listen for multiple responsive data defined by the REF at the same time
You can use an array to listen for more than one response data defined by the REF at the same time.
<template>
<input type="text" v-model="name" />
<input type="text" v-model.number="age" />
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'App'.setup() {
const name = ref('Joe')
const age = ref(20)
// newValue and oldValue are arrays that correspond to the order of the listening variables
watch([name, age], (newValue, oldValue) = > {
console.log(newValue) // [newName, newAge]
console.log(oldValue) // [oldName, oldAge]
})
return {
name,
age
}
}
}
</script>
Copy the code
Listen for reactive data defined by reactive
When monitoring a reactive object defined by reactive, the oldValue cannot be correctly obtained. Because listening to a responsive object or array always returns the object’s current value (newValue) and a reference to its last state value (oldValue).
<template>
App
</template>
<script>
import { reactive, watch } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})Listening for a reactive object or array always returns a reference to the current value and the last state value of the object
watch(person, (newValue, oldValue) = > {
console.log(newValue === oldValue) // true
console.log(newValue.name === oldValue.name) // true
})
person.name = 'bill'
return { person }
}
}
</script>
Copy the code
To fully listen for deeply nested objects and arrays, you need to do a deep copy of the values. This can be done by using a method such as lodash.clonedeep and returns as a getter function:
watch(
() = > _.cloneDeep(person),
(newValue, oldValue) = > {
console.log(newValue === oldValue) // false
console.log(newValue.name === oldValue.name) // false})Copy the code
Listen to a reactive object defined by reactive. There are two cases:
- If the first argument passed is a responsive property. The default is deep listening, and set
{deep:false}
Is invalid. - Normal configuration if the first argument passed is a function.
<template>
App
</template>
<script>
import { reactive, watch } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})// Pass it as an object
watch(
person,
(newValue, oldValue) = > {
console.log(newValue.job.jobName) / / designer
},
{ deep: false } // Deep is always true, false is invalid
)
person.job.jobName = 'Designer'
const book = reactive({
bookName: 'Advanced Programming in JavaScript'.category: 'IT'.area: {
areaName: 'Beijing'}})// Pass in the return value of the function
watch(
() = > book,
(newValue, oldValue) = > {
console.log(newValue.area.areaName) // No output
},
{ deep: false } // The configuration is valid
)
book.area.areaName = 'Shanghai'
return { person, book }
}
}
</script>
Copy the code
If an attribute in a reactive object defined by monitoring reactive is a basic data type, the attribute is returned by a callback function.
<template>
App
</template>
<script>
import { reactive, watch } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'.area: {
areaName: 'Shanghai'}}})// The base data type is returned as a function, otherwise invalid
watch(
() = > person.name,
(newValue, oldValue) = > {
console.log(newValue) / / li si
}
)
person.name = 'bill'
// Reference type
watch(
() = > person.job,
(newValue, oldValue) = > {
console.log(newValue.area.areaName) / / Beijing
},
{ deep: true }
)
person.job.area.areaName = 'Beijing'
return { person }
}
}
</script>
Copy the code
Listen for multiple reactive data defined by reactive
To monitor some properties of a reactive object defined by reactive, the listening properties are passed in through an array.
<template>
App
</template>
<script>
import { reactive, watch } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'.area: {
areaName: 'Shanghai'
}
}
})
watch(
[() = > person.name, () = > person.age, () = > person.job],
(newValue, oldValue) = > {
console.log(newValue)
},
{ deep: true }
)
person.name = 'bill'
person.age = 30
person.job.jobName = 'Designer'
return { person }
}
}
</script>
Copy the code
watchEffect
The watchEffect function does not specify which property to listen on; it listens on which property is used in the listening callback. The default initialization is performed once.
<template>
<input type="text" v-model="person.name" />
<input type="text" v-model="person.job.jobName" />
</template>
<script>
import { reactive, watchEffect } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'.area: {
areaName: 'Shanghai'
}
}
})
watchEffect(() = > {
console.log(person.name)
console.log(person.job.jobName)
})
return { person }
}
}
</script>
Copy the code
The life cycle
Vue3 has modified the life cycle, as shown below from the official website:
<template>
App
</template>
<script>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onActivated,
onDeactivated,
onErrorCaptured
} from 'vue'
export default {
beforeCreate() {
console.log('Before Create! ')},created() {
console.log('Create! ')},setup() {
console.log('setup()')
onBeforeMount(() = > {
console.log('Before Mount! ')
})
onMounted(() = > {
console.log('Mounted! ')
})
onBeforeUpdate(() = > {
console.log('Before Update! ')
})
onUpdated(() = > {
console.log('Updated! ')
})
onBeforeUnmount(() = > {
console.log('Before Unmount! ')
})
onUnmounted(() = > {
console.log('Unmounted! ')
})
onActivated(() = > {
console.log('Activated! ')
})
onDeactivated(() = > {
console.log('Deactivated! ')
})
onErrorCaptured(() = > {
console.log('Error Captured! ')}}}</script>
Copy the code
Reference: v3.cn.vuejs.org/api/options…
ToRef and toRefs
ToRef can create a new REF for an attribute on a reactive object.
ToRefs and toRef function the same, you can create multiple toRef objects.
If objects are nested at a very deep level and are used in templates, it is necessary to use the obj. Property form, and you can use toRef and toRefs to simplify development:
<template>
<p>{{ name }}</p>
<p>{{ areaName }}</p>
<p>{{ bookName }}</p>
<input v-model="areaName" />
</template>
<script>
import { reactive, toRef, toRefs } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'.area: {
areaName: 'Shanghai'}}})const book = reactive({
bookName: 'Advanced Programming in JavaScript'.category: 'IT'
})
/* Object properties become reactive, unbunding objects to simplify the use of */
return {
name: toRef(person, 'name'),
areaName: toRef(person.job.area, 'areaName'),
...toRefs(book)
}
}
}
</script>
Copy the code
ReadOnly and shallowReadOnly
-
Readonly: Makes a reactive data read-only (deep read-only).
-
ShallowReadOnly: Makes a reactive data read-only (shallow read-only).
You can use them if you don’t want the data to be modified:
<template>
<div>
<p>{{ rPerson.job.jobName }}</p>
<! -- Not reactive and cannot be modified -->
<input type="text" v-model="rPerson.job.jobName" />
</div>
<div>
<p>{{ srPerson.job.jobName }}</p>
<! -- Is reactive and can be modified -->
<input type="text" v-model="srPerson.job.jobName" />
</div>
</template>
<script>
import { reactive, readonly, shallowReadonly } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})// Cannot be modified
const rPerson = readonly(person)
// Only the first layer of data is considered and cannot be modified
const srPerson = shallowReadonly(person)
return {
rPerson,
srPerson
}
}
}
</script>
Copy the code
ToRaw and markRaw
ToRaw: Turns a reactive object generated by reactive into a regular object.
MarkRaw: Marks an object so that it will never be a responsive object again.
<template>
App
</template>
<script>
import { reactive, toRaw, markRaw } from 'vue'
export default {
name: 'App'.setup() {
const person = reactive({
name: 'Joe'.age: 20.job: {
jobName: 'Engineer'}})const rawPerson = toRaw(person) // Return a primitive object
person.car = markRaw({ carName: 'audi'.price: '300000' }) // Never convert to proxy
return {
person,
rawPerson
}
}
}
</script>
Copy the code
customRef
Create a custom REF with explicit control over its dependency tracking and update triggering.
CustomRef needs to return a function that takes the track and trigger functions as parameters and should return an object with get and set.
<template>
<input type="text" v-model="keyword" />
<p>{{ keyword }}</p>
</template>
<script>
import { customRef } from 'vue'
export default {
name: 'App'.setup() {
// Custom ref, delay update by 1 second
function myRef(value, delay = 1000) {
return customRef((track, trigger) = > {
return {
get() {
track() // Notify Vue to track value changes
return value
},
set(newValue) {
setTimeout(() = > {
value = newValue
trigger() // Tell Vue to rerender the template
}, delay)
}
}
})
}
let keyword = myRef('hello'.1000)
return {
keyword
}
}
}
</script>
Copy the code
Dojo.provide and inject
Provide and Inject implement communication between grandparent and grandchild components. The parent component has a provide option to provide the data and the child component has a Inject option to start using the data. The following picture is from the official website:
// App.vue
<template>
App
<child />
</template>
<script>
import { reactive, provide } from 'vue'
import Child from './components/Child'
export default {
name: 'App'.components: { Child },
setup() {
const person = reactive({
name: 'Joe'.age: 20
})
provide('person', person) // Pass data to the descendant component
return {
person
}
}
}
</script>
Copy the code
// components/child.vue
<template>
<p>The child components</p>
<son />
</template>
<script>
import son from './son'
export default {
components: { son }
}
</script>
Copy the code
// components/son.vue
<template>
<p>The grandson components</p>
<p>{{ person.name }}</p>
</template>
<script>
import { inject } from 'vue'
export default {
setup() {
const person = inject('person') // Receive data from upper-layer components
console.log(person) // Proxy {name: "name ", age: 20}
return {
person
}
}
}
</script>
Copy the code
Reactive data judgment
IsRef: Checks whether a value is a ref object.
IsReactive: Checks whether a reactive proxy is created by reactive.
IsReadonly: Checks whether an object is a read-only agent created by readonly.
IsProxy: Checks whether an object is a proxy created by the reactive or readonly methods.
<template>
App
</template>
<script>
import {
ref,
reactive,
readonly,
isRef,
isReactive,
isReadonly,
isProxy
} from 'vue'
export default {
name: 'App'.setup() {
const msg = ref('A message')
const person = reactive({ name: 'Joe'.age: 20 })
const readonlyPerson = readonly(person) Readonly The returned value is still a proxy object
console.log(isRef(msg)) // true
console.log(isReactive(person)) // true
console.log(isReadonly(readonlyPerson)) // true
console.log(isProxy(person)) // true
console.log(isProxy(readonlyPerson)) // true
return {
msg,
person
}
}
}
</script>
Copy the code
Custom instruction
In Vue3, custom directive names have been changed and some methods have been added, as follows:
created
Called after the element is created, but the attributes and events have not yet taken effect.beforeMount
: called when the directive is first bound to an element and before the parent element is mounted.mounted
: called when an element is mounted to its parent element.beforeUpdate
: new! This is called before the component itself is updated.updated
: called after a component or child component has been updated.beforeUnmount
: called before the element is unloaded.unmounted
: Called only once when the instruction is unloaded.
Here’s an example:
// App.vue
<template>
<div v-if="isShow" class="parent-title">
<h1 v-title class="title">{{ titleMsg }}</h1>
<p>{{ p }}</p>
</div><! Output beforeUpdate and updated--><button @click="TitleMsg = 'new title '">Update the title</button><! Output beforeUpdate and updated--><button @click="P = 'new paragraph '">Update the paragraph</button><! BeforeUnmount, Unmounted --><button @click="isShow = false">Uninstall the component</button>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App'.setup() {
const titleMsg = ref('title')
const p = ref('section')
const isShow = ref(true)
return {
titleMsg,
p,
isShow
}
}
}
</script>
Copy the code
// Customize the main.js directive
const app = createApp(App)
app.directive('title', {
created(el, binding, vnode) {
console.log(el.className) // The value is an empty string because neither the property nor the event is in effect.
},
beforeMount(el, binding, vnode) {
console.log(el.className) // The value is title and the element has been created completely.
console.dir(el.parentNode) // The value is null because the parent node has not been inserted
},
mounted(el, binding, vnode) {
console.log(el.parentNode) // The value is the parent element div. Parent-title, because the element is inserted into the parent element
},
beforeUpdate(el, binding, vnode) {
console.log('beforeUpdate')},updated(el, binding, vnode) {
console.log('update')},beforeUnmount(el, binding, vnode) {
console.log('beforeUnmount')},unmounted(el, binding, vnode) {
console.log('unmounted')}})Copy the code
The function is called when the same behavior is triggered when Mounted and updated:
const app = createApp(App)
app.directive('font-size'.(el, binding, vnode) = > {
el.style.fontSize = binding.value + 'px'
})
Copy the code
Teleport components
A Teleport component is a technology that allows you to move a component’S HTML structure to a specified location.
For example, a component wants to insert it under the body element:
<template>
App
<teleport to="body">
<div>Insert it under the body element as a child of the body element</div>
</teleport>
</template>
<script>
export default {
name: 'App'.setup(){}}</script>
Copy the code
Test the Suspense component
Suspense components can render some backup content while waiting for asynchronous components. For example, add loading animation.
The suspense> component has two slots. Nodes in the default slot are displayed as much as possible. If not, show the node in the fallback slot.
// App.vue
<template>
<h1>App</h1>
<Suspense>
<template #default>
<child />
</template>
<template #fallback>
<p>Asynchronous component, loading....</p>
</template>
</Suspense>
</template>
<script>
// Import child from './components/child.vue' // Static import
import { defineAsyncComponent } from 'vue'
const Child = defineAsyncComponent(() = > import('./components/Child')) // Asynchronous import
export default {
components: { Child },
name: 'App'.setup(){}}</script>
Copy the code
// components/Child.vue
<template>
<p>The Child Child components</p>
</template>
<script>
export default {
name: 'Child'.setup() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve()
}, 2000)}}}</script>
Copy the code
Setup () can return a Promise object when a component is introduced asynchronously.
Other adjustments
Global API
Vue3 adjusts the global API vue. XXX to the instance (app) as follows:
2. X global API | 3.0 global API (app) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | remove |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
Vue.prototype | app.config.globalProperties |
The data options
The data option should always declare a function, not an object.
Transition animation class name changed
Vue3 animation class name v-Enter changed to V-enter -form and V-leave changed to V-leave-FROM.
Vue2 writing:
/* The starting point of entry */
.v-enter {}
/* The process of entry */
.v-enter-active {}
/* End of entry */
.v-enter-to {}
/* The starting point of departure */
.v-leave {}
/* The process of leaving */
.v-leave-active {}
/* The end of leaving */
.v-leave-to {}
Copy the code
Vue3 writing:
/* The starting point of entry */
.v-enter-from {}
/* The process of entry */
.v-enter-active {}
/* End of entry */
.v-enter-to {}
/* The starting point of departure */
.v-leave-from {}
/* The process of leaving */
.v-leave-active {}
/* The end of leaving */
.v-leave-to {}
Copy the code
Vue3 remove item
-
Removed keycode as a modifier for V-ON, and config.keyCodes are no longer supported.
-
Filter filter has been deleted and is no longer supported. The official documentation suggests replacing them with method calls or computed properties.
-
Remove the v-on. Native modifier.
// The parent component defines the event
<template>
<h1>App</h1>
<child @close="handleClose" />
</template>
// The child component declares custom events
<script>
export default {
name: 'Child'.emits: ['close']}</script>
Copy the code
Here are the more important points, also need to see the official document in detail: v3.cn.vuejs.org/guide/migra…
Combined function
The setup function can extract the code of each function into a separate JS file, which is more convenient to reuse. Mixin similar to Vue2.
// hooks/usePoint.js
import { reactive, onMounted, onBeforeUnmount } from 'vue'
// Click the page to get the mouse position function
export default function() {
const point = reactive({
x: 0.y: 0
})
function clickHandle({ pageX, pageY }) {
point.x = pageX
point.y = pageY
}
onMounted(() = > {
window.addEventListener('click', clickHandle)
})
onBeforeUnmount(() = > {
window.removeEventListener('click', clickHandle)
})
return point
}
Copy the code
// App.vue<template> App <h2> my mouse position is X:{{point. X}},Y:{{ point.y }}</h2>
</template>
<script>
import usePoint from './hooks/usePoint'
export default {
name: 'App'.setup() {
/ / introduce usePoint
const point = usePoint()
return { point }
}
}
</script>
Copy the code
reference
V3.cn.vuejs.org/guide/compo…
zhuanlan.zhihu.com/p/68477600
www.bilibili.com/video/BV1Zy…