I wrote more than 30,000 words in the last article, and I had to delay typing for a while. Now the text can be displayed in real time, which feels so silky haha.
Table of contents
8. Vue componentization - communication between parent and child components
8.1. Parent component passed to child component
8.2. Passing child components to parent components
9. Vue Componentization - Slot Slot
9.3. Effect of Multiple Sockets
9.5. Dynamic slot names (understand)
9.6. Syntactic sugar for named slots
10. Vue componentization - non-parent-child component communication
10.2. Function writing of Provide and Inject
11. Vue componentization - additional knowledge supplement
11.1.1. Component Lifecycle Walkthrough
11.2. ref references in components
11.3. Use of dynamic components
11.3.1. Display different components with v-if
11.3.2. Realization of dynamic components
11.4.2. The life cycle of the cache component
11.5.1. Implement asynchronous components in Vue
12.1. Understanding the Composition API
12.2.Parameters of setup function
12.3. The return value of the setup function
12.7. API for Reactive judgment (understand)
12.11.computed computed property
12.13. Life cycle in setup function
12.14. Provide function and Inject function
12.15.4.watchEffect stops listening
12.16.1. Top-level bindings are exposed to templates
12.16.2. Imported components are used directly
12.16.3.defineProps()和defineEmits()
If you don’t understand anything, you can check the Vue.js official website
8. Vue componentization - communication between parent and child components
- In the previous case, we just created a component App ;
- If our application puts all the logic in one component, then this component will become very bloated and difficult to maintain ;
- Therefore, the core idea of componentization should be to split the components into small components ;
- Then combine and nest these components together to form our application ;
In real development, we can split the project.
- The App component is the parent component of the Header, Main, and Footer components ;
- The Main component is the parent component of the Banner and ProductList components ;
- For example, the app may use multiple headers , and the content displayed by the headers in each place is different , so we need the user to pass some data to the header for display;
- Another example is that if we request the Banner data and ProductList data at one time in Main , we need to pass them to them for display;
- It may also be that an event has occurred in the child component , and some operations need to be completed by the parent component , then the child component needs to pass the event to the parent component ;
Passing between parent and child components is a very important way of communication.
So how to communicate between parent and child components?
- The parent component is passed to the child component: through the props attribute ;
- The child component is passed to the parent component: the event is triggered through $emit .
8.1. Parent component passed to child component
It is very common in development to communicate between parent and child components . For example, the parent component has some data that needs to be displayed by the child component: at this time, we can use props to complete the communication between components ;
- Props is that you can register some custom attributes on the component ;
- The parent component assigns values to these attributes , and the child components obtain the corresponding values through the attribute name ;
<!-- App.vue -->
<template>
<cpn name="zzz" height="1.7" age="18"></cpn>
</template>
<script>
import cpn from './components/cpn.vue'
export default {
data() {
return {
}
},
components: {
cpn:cpn
}
}
</script>
<style scoped>
</style>
- In the App.vue parent component file, the components object defines a component cpn ;
- Use import to introduce the location of the cpn file of the component , and use the cpn tag in the template ;
- Fill in the attributes and values to be passed to the subcomponent in the cpn tag , and receive it in the subcomponent cpn.vue.
<!-- cpn.vue -->
<template>
<div class="infos">
<h2>姓名:{
{name}}</h2>
<h2>年龄:{
{age}}</h2>
<h2>身高:{
{height}}</h2>
</div>
</template>
<script>
export default {
props:["name", "age", "height"]
}
</script>
<style scoped>
</style>
- Use the props attribute to receive the incoming attribute attribute name ;
- Use the incoming value in the template . If the value used is not defined in the props attribute, it will go to data to find it.
Props have two common uses:
- Method 1: String array , the string in the array is the name of the attribute;
- Method 2: Object type , object type We can specify the type of attribute it needs to pass, whether it is necessary, the default value, etc. while specifying the attribute name;
The above demonstration uses the first method, and the following two usages are explained.
8.1.1. Array usage of props
The props of the child component receive the attribute passed by the parent component, which needs to be one-to-one correspondence, and the string array is saved in the props.
This kind of array usage can only explain the name of the attribute passed in , and cannot restrict it in any form , so in actual development, the object usage of props is more used.
8.1.2. Object usage of props
When using the object syntax, we can be more restrictive about what is passed in:
- Specify the type of attribute passed in ;
- Specifies whether the incoming attribute is mandatory ;
- Specifies the default value of the attribute when no input is specified .
props:{
name: {
type: String,
required: true,
default: "z"
},
age: {
type: Number,
required: false,
default: 10
},
height: {
type: Number,
required: false,
default: 1.00
}
}
Generally, when the attribute is not mandatory, the default value is not written.
type of type
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
Other ways of writing object types
props:{
name: String,//基础的类型检测
age: [String, Number],//多个可能的类型
propO:{
type: Object,//对象和数组的默认值必须从一个工厂函数获取
default(){
return { message: "message"}
}
}
}
Prop case naming
The attribute name in HTML is case-insensitive , so the browser will interpret all uppercase characters as lowercase characters ;
This means that when you use templates in the DOM , camelCase (camel case) prop names need to be named using their equivalent kebab-case (dash-separated names) ;
8.1.3. attribute inheritance
non-prop attribute
Before understanding attribute inheritance, you need to understand the concept of non-prop attributes .
- When we pass a property to a component , but the property does not define the corresponding props or emits , it is called a non- Prop Attribute ;
- Common ones include class, style, id attributes, etc.;
Attribute inheritance
<!-- 父组件传递了class和id App.vue -->
<template>
<cpn class="cpn" id="cpn1" name="zzz" height="1.7" age="18"></cpn>
</template>
<script>
import cpn from './components/cpn.vue'
export default {
data() {
return {
}
},
components: {
cpn:cpn
}
}
</script>
<style scoped>
</style>
<!-- cpn.vue -->
<template>
<div class="infos">
<h2>姓名:{
{name}}</h2>
<h2>年龄:{
{age}}</h2>
<h2>身高:{
{height}}</h2>
</div>
</template>
<script>
export default {
props:{
name: String,
age: [String, Number],
propO:{
type: Object,
default(){
return { message: "message"}
}
}
}
}
</script>
<style scoped>
</style>
You can see that the root node of the child component inherits the non-prop attribute passed from the parent component .
Disable attribute inheritance
//cpn.vuecpn.vue
export default {
inheritAttrs:false,
}
- A common case of disabling attribute inheritance is the need to apply attributes to elements other than the root element ;
- We can access all non-props attributes through $attrs ;
<template>
<div class="infos">
<h2 :class="$attrs.class">姓名:{
{name}}</h2>
<h2>年龄:{
{age}}</h2>
<h2>身高:{
{height}}</h2>
</div>
</template>
Attributes of multiple root nodes
If there are no displayed bindings for the attributes of multiple root nodes , a warning will be reported, and we must manually specify which attribute to bind to .
8.2. Passing child components to parent components
There are cases where the parent component is passed to the child component, and there is also a case where the child component is passed to the parent component.
- For example, when some events occur in the child component , such as a click in the component, the parent component needs to switch the content ;
- When the child component has some content that it wants to pass to the parent component ;
Now write a case: the parent component displays the number of the counter, and the child component is responsible for increasing the quantity button.
<!-- cpn.vue 子组件-->
<template>
<div>
<!-- 1.子组件触发事件 -->
<button @click="addCount(1)">+1</button>
<button @click="addCount(5)">+5</button>
<button @click="addCount(10)">+10</button>
</div>
</template>
<script>
export default {
methods: {
// 2.子组件将自定义的事件名add和参数count通过this.$emit()传递出去
addCount(count) {
this.$emit("add", count)
}
}
}
</script>
<style scoped>
</style>
1. The subcomponent triggers an event;
2. The subcomponent passes the custom event name add and the parameter count through this.$emit();
<!-- App.vue 父组件 -->
<template>
<div>
<div class="count">当前计数{
{counter}}</div>
<!-- 3.父组件使用自定义的add事件,并且编写方法 -->
<cpn @add="addClick"></cpn>
</div>
</template>
<script>
import cpn from './components/cpn.vue'
export default {
data() {
return {
counter: 0
}
},
components: {
cpn:cpn
},
methods: {
// 4.父组件对自定义事件触发的方法进行处理
addClick(count) {
this.counter += count
}
},
}
</script>
<style scoped>
</style>
3. The parent component uses a custom add event and writes the method;
4. The parent component processes the method triggered by the custom event.
8.2.1. emit property
In the subcomponent, you can first register the passed event name in the attribute. Parent components will be prompted when using custom events.
export default {
emits:["add"],
methods: {
// 2.子组件将自定义的事件名add和参数count通过this.$emit()传递出去
addCount(count) {
this.$emit("add", count)
}
}
}
9. Vue Componentization - Slot Slot
- Earlier, we will pass some data to the component through props, and let the component display it;
- But in order to make this component more versatile , we cannot limit the content in the component to fixed div, span, etc. elements;
- For example, in some cases we use a component and hope that the component displays a button , and in some cases we use a component and hope to display a picture ;
- We should allow users to decide what content and elements are stored in a certain area ;
- 1 This component is divided into three areas: left-middle-right , and the content of each area is not fixed;
- The left area may display a menu icon, or a back button, or nothing;
- The middle area may display a search box, a list, a title, etc.;
- The right side may be a text, or an icon, or nothing;
At this time we can define the slot slot:
- The process of using slots is actually extracting commonality and reserving differences ;
- We will still encapsulate the common elements and content in the component ;
- At the same time, slots will be used as placeholders for different elements , allowing the outside world to decide what elements to display;
9.1. Basic usage of slot
How to use slots?
- In Vue, the <slot> element is used as the outlet for carrying and distributing content ;
- In the package component, use the special element <slot> to open a slot for the package component ;
- What the slot inserts depends on how the parent component uses it;
It is a bit difficult to understand, but it will be clearer with an example.
App.vue is the parent component, and slotContent.vue is the child component. There are slots in slotContent. Adding some elements to the sub-component tag of App will add them to the slot of the sub-component by default.
9.2. Default content of slot
If the parent component does not add content to the slot when calling the child component, the default value of the child component slot will be used.
9.3. Effect of Multiple Sockets
If we want to make a top menu bar, we need to divide the slot into three parts, left, middle and right. If according to the above writing method will have what kind of effect.
Will the three elements of App.vue correspond to the slot of slotContent.vue in turn?
will not match, but the same content will be displayed three times. Then how can we correspond to it? We need to use named slots to achieve it.
9.4. Use of named slots
- As the name suggests, a named slot is to give the slot a name , and the <slot> element has a special attribute: name ;
- A slot without a name will have the implicit name default ;
<!-- App.vue-->
<template>
<div>
<slot-content title="标题">
<template v-slot:left>
<button >left</button>
</template>
<template v-slot:center>
<h2>center</h2>
</template>
<template v-slot:right>
<h3>right</h3>
</template>
</slot-content>
</div>
</template>
<script>
import slotContent from './components/slotContent.vue'
export default {
components: {
slotContent: slotContent
},
}
</script>
<!-- slotContent.vue -->
<template>
<div>
<h2>{
{title}}</h2>
<div>
<div class="left">
<slot name="left">
</slot>
</div>
<div class="center">
<slot name="center">
</slot>
</div>
<div class="right">
<slot name="right">
</slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type:String
},
content: {
type: String
}
}
}
</script>
- Add a name to the slot element of the child component so that the parent component can use it;
- Wrap the content to be inserted into the subcomponent slot with a template element ;
- Use v-slot:name to correspond to the slot position of the subcomponent, must use: to connect the slot name .
Emphasize again the error-prone point: use template element to wrap; v-slot and name are separated by :.
9.5. Dynamic slot names (understand)
We can dynamically bind a name through v-slot:[dynamicSlotName] ;
You can put the variables in [] in data.
9.6. Syntactic sugar for named slots
- Like v-on and v-bind, v-slot also has abbreviations ;
- That is, replace everything before the parameter (v-slot:) with the character # ;
<template>
<div>
<slot-content title="标题">
<template #left>
<button >left</button>
</template>
<template #center>
<h2>center</h2>
</template>
<template #right>
<h3>right</h3>
</template>
</slot-content>
</div>
</template>
After this modification, the effect of #left is the same as that of v-slot:left.
9.7. Scoped slots (important)
This concept is quite difficult, through a case to illustrate.
<!--App.vue-->
<template>
<div>
<slot-content :names="names">
<template v-slot:default="slotProps">
<span>{
{slotProps.item}}-{
{slotProps.index}}</span>
</template>
</slot-content>
</div>
</template>
<script>
import slotContent from './components/slotContent.vue'
export default {
data() {
return {
names: ["apple","banana","cat","dog"]
}
},
components: {
slotContent: slotContent
},
}
</script>
<!--slotContent.vue-->
<template>
<div>
<div v-for="(item, index) in names" :key="item">
<slot :item="item" :index="index"></slot>
</div>
</div>
</template>
<script>
export default {
props: {
names: {
type:Array
}
}
}
</script>
- Define the data in App.vue;
- Data is passed to child components;
- Traverse the names data in the subcomponent;
- Define the props of the slot;
- Obtain slot props through v-slot:default;
- Use item and index in slotProps.
The parent component passes an array to the child component, and the child component traverses the array through the v-for loop. When looping the array, there are item and index. The item and index are passed to the parent component by defining the props of the slot slot. The parent component uses v -slot: default received, the parent component can use item and index.
10. Vue componentization - non-parent-child component communication
In development, after we build the component tree, in addition to the communication between parent and child components , there will also be communication between non-parent and child components .
Vue is a stateful management library to manage and realize data sharing, such as vuex, pinia , and learn later.
What is learned here is to achieve communication without relying on third-party management libraries.
10.1. Provide and Inject
It is used less during development.
- For example, there are some deeply nested components , and the child component wants to get part of the content ;
- In this case, if we still pass props down the component chain level by level, it will be very troublesome;
- No matter how deep the hierarchy is, a parent component can act as a dependency provider for all its child components ;
- The parent component has a provide option to provide data;
- Child components have an inject option to start using this data;
- The parent component does not need to know which child components use the property it provides;
- Child components don't need to know where the injected property comes from.
We develop a structure App.vue as the parent component of Home.vue, and Home.vue as the parent component of HomeContent.vue.
<!--App.vue-->
<template>
<div>
<home></home>
</div>
</template>
<script>
import Home from './components/Home.vue'
export default {
components: {
Home
},
provide: {
name: "zzz",
age: 18
}
}
</script>
<!--Home.vue-->
<template>
<home-content></home-content>
</template>
<script>
import HomeContent from './HomeContent.vue'
export default {
data() {
return {
}
},
components: {
HomeContent
}
}
</script>
<!--HomeContent.vue-->
<template>
<div>AppContent
<h2>{
{name}}</h2>
<h2>{
{age}}</h2>
</div>
</template>
<script>
export default {
inject: ["name", "age"]
}
</script>
In this way, the data defined in App.vue can be used in HomeContent.vue.
10.2. Function writing of Provide and Inject
If the data provided in provide comes from data , we may want to get it through this ;
But this will report an error, and the names will not be found.
So our provide needs to use function writing . Similar to data, returns an object.
<!--App.vue-->
<template>
<div>
<home></home>
</div>
</template>
<script>
import Home from './components/Home.vue'
export default {
data() {
return {
names: ["aaa", "bbb"]
}
},
components: {
Home
},
provide() {
return{
name: "zzz",
age: 18,
length: this.names.length
}
}
}
</script>
11. Vue componentization - additional knowledge supplement
11.1. Component life cycle
You can take a look at Muzi Mumu's vue3 life cycle and life cycle function explanation, which is very well explained. Of course, you can also look directly down.
What is the life cycle?
- In biology, the biological life cycle refers to a series of changes that an organism goes through from the beginning to the end of life;
- Each component may go through a series of processes from creation, mounting, updating, uninstalling, etc.;
- At a certain stage in this process , we may want to add some code logic of our own (such as requesting some server data after the component is created);
- But how can we know which process the component is currently in ? Vue provides us with component lifecycle functions ;
- Life cycle functions are some hook functions (callback functions) , which will be called back by the Vue source code at a certain time ;
- Through the callback of the life cycle function, we can know what stage the component is currently going through ;
- Then we can write our own logic code in this life cycle ;
beforeCreate
1. Create a component instance
created (send network request event listener)
2. template template compilationbeforeMount
3. Mount to virtual DOM, virtual DOM->real DOM
mounted (the element has been mounted to get the DOM to use the DOM)
4. Data update
beforeUpdate
Generate a new VNode based on the latest data, generate a new virtual DOM -> generate a new real DOM
updated
5. No longer use such as v-if="false"
beforeUnmount
Remove the VNode previously mounted in the virtual DOM from the virtual DOM
unmounted (cancel event listener)
Destroy the component instance
I feel that the life cycle is a bit too much, so focus on understanding the life cycle in red above .
11.1.1. Component Lifecycle Walkthrough
export default {
// 1.组件被创建之前
beforeCreate() {
console.log("beforeCreate")
},
// 2.组件被创建完成
created() {
console.log("created")
console.log("1.发送网络请求,请求数据")
console.log("2.监听事件")
console.log("3.监听watch数据")
},
// 3.组件template准备被挂载
beforeMount() {
console.log("beforeMount")
},
// 4.组件template被挂载: 虚拟DOM -> 真实DOM
mounted() {
console.log("mounted")
console.log("1.获取DOM")
console.log("2.使用DOM")
},
// 5.数据发生改变 准备更新DOM
beforeUpdate() {
console.log("beforeUpdate")
},
// 6.更新DOM
updated() {
console.log("updated")
},
// 7.卸载VNode -> DOM元素 卸载之前
beforeUnmount() {
console.log("beforeUnmount")
},
// 8.DOM元素被卸载完成
unmounted() {
console.log("beforeUnmount")
},
}
11.2. ref references in components
- We do not recommend DOM manipulation in Vue development ;
- At this time, we can bind a ref attribute to the element or component ;
<template>
<div>
<h2 ref="title">获取的h2
</h2>
</div>
</template>
Component instances have a $refs property:
- It is an Object that holds all DOM elements and component instances registered with the ref attribute .
export default {
methods: {
changeTitle() {
console.log(this.$refs.title);
}
}
}
- Here we can also achieve it through $root , because App is our root component;
- Note: The $children property has been removed in Vue3 , so it can no longer be used.
11.3. Use of dynamic components
To achieve dynamic switching component display:
- It can be judged by v-if to display different components;
- Dynamic components can also be used .
11.3.1. Display different components with v-if
Use v-if to judge currentIndex to decide which component to use.
<template>
<div>
<template v-for="(item, index) in tabs" :key="index">
<button :class="{active: currentIndex === index}"
@click="btnClick(index)">{
{item}}
</button>
</template>
<template v-if="currentIndex === 0">
<home></home>
</template>
<template v-else-if="currentIndex === 1">
<about></about>
</template>
<template v-else-if="currentIndex === 2">
<category></category>
</template>
</div>
</template>
<script>
import Home from './components/Home.vue'
import About from './components/About.vue'
import Category from './components/Category.vue'
export default {
data() {
return {
tabs: ["home", "about", "category"],
currentIndex: 0
}
},
components: {
Home,
About,
Category
},
methods: {
btnClick(index){
this.currentIndex = index
}
}
}
</script>
<style scoped>
.active{
color:red;
}
</style>
11.3.2. Realization of dynamic components
- Global registration: it can be a component registered through the component function ;
- Layout registration: the components registered in the components object of a component object ;
Automatically find components according to the content in is.
If it is a dynamic component, can we pass values to them and listen to events?
- Yes, we put attributes and listener events on the component to use;
11.4. Use of keep-alive
If you add a counter to the home of the component switching case above, the number can be increased by clicking the button.
Will it be retained after clicking to about and then returning to the home state?
- The answer is no ;
- This is because by default, after we switch components , the home component will be destroyed , and the component will be recreated when we come back again ;
However, in some cases during development, we want to keep the state of the component instead of destroying it. At this time, we can use a built-in component: keep-alive .
11.4.1. keep-alive property
- include - string | RegExp | Array. Only components with matching names will be cached;
- exclude - string | RegExp | Array. Any component whose name matches will not be cached;
- max - number | string. The maximum number of component instances that can be cached. Once this number , the instances in the cache component that have not been accessed recently will be destroyed;
<!-- 逗号分隔字符串 -->
<keep-alive include="home,about">
<component :is="tabs[currentIndex]"></component>
</keep-alive>
<!-- regExp(使用v-bind) -->
<keep-alive :include="/home|about/">
<component :is="tabs[currentIndex]"></component>
</keep-alive>
<!-- Array (使用v-bind) -->
<keep-alive :include="['home','about']">
<component :is="tabs[currentIndex]"></component>
</keep-alive>
- Both can be represented as comma-separated strings, regular expressions, or an array ;
- The match first checks the name option of the component itself ;
Matching first checks the name option on the component itself.
So the name in the component is very important, if there is no name, there is no way to cache.
11.4.2. The life cycle of the cache component
For cached components, when re-entering, we will not execute life cycle functions such as created or mounted :
- But sometimes we really want to monitor when we re-enter the component and when we leave the component;
- At this time, we can use the two life cycle hook functions activated and deactivated to monitor;
11.5. Webpack code subpackage
The default packaging process:
- By default, in the process of building the entire component tree, because components are directly dependent on modules , webpack will package component (for example, in an app.js file) ;
- At this time, as the project continues to grow , the content of the app.js file is too large , which will slow down the rendering speed of the first screen ;
How to package in vue?
- Use the command npm run build on the command line, and a dist file will appear;
npm run build
When packaging, the subpackage of the code:
- Therefore, for some components that do not need to be used immediately , we can split them separately and split them into some small code blocks chunk.js ;
- These chunk.js will be loaded from the server when needed , and run the code to display the corresponding content;
So how can the code be subpackaged in webpack?
Use the import function to import files in main.js to realize the subpackage of the code.
import("../utils/math").then( res => {
res.sum(20, 30)
})
11.5.1. Implement asynchronous components in Vue
If our project is too large and we want to load some components asynchronously (the purpose is to subcontract them), then Vue provides us with a function: defineAsyncComponent .
- Type 1 : Factory function, which needs to return a Promise object;
- Type 2 : Accept an object type and configure the asynchronous function; (understand)
11.6. v-model for components
- It is often very convenient at this time, because v-model helps us accomplish two things by default;
- v-bind:value data binding and @input event monitoring ;
- It is also possible, vue also supports the use of v-model on components ;
- When we use it on a component, it is equivalent to the following operations:
- We will find that the only difference from the input element is the name of the attribute and the name of the event trigger ;
<!--App.vue-->
<template>
<div>
<!-- <home v-model="message"></home> -->
<home :modelValue="message" @update:modelValue="this.message = $event"></home>
</div>
</template>
<script>
import Home from './components/Home.vue'
export default {
data() {
return {
message:0
}
},
components: {
Home,
},
}
</script>
<!--Home.vue-->
<template>
<div>home
<h4>{
{ modelValue }}</h4>
<button @click="btnClick">改变</button>
</div>
</template>
<script>
export default {
name: "home",
methods: {
btnClick() {
this.$emit("update:modelValue", 100)
}
},
props: {
modelValue: {
type:Number,
default: 0
}
}
}
</script>
11.7. Use of Mixins
- Mixin provides a very flexible way to distribute reusable functions in Vue components ;
- A Mixin object can contain any component options ;
- When a component uses a Mixin object, all the options of the Mixin object will be mixed into the options of the component itself ;
11.7.1. Mixin Merging Rules
- Return value objects are merged by default ;
- If the properties of the data return value object conflict, the data of the component itself will be retained ;
- The hook functions of the life cycle will be merged into the array and will be called;
- For example, if there are methods options and methods are defined, they will all take effect ;
- But if the key of the object is the same , then the key-value pair of the component object will be taken ;
11.7.2. Global Mixin Mixin
- The global Mixin can use the method mixin of the application app to complete the registration;
- Once registered, the global mix-in options will affect every component ;
12.Vue3 - CompositionAPI
- A major feature of the Options API is to write the corresponding functional modules in the corresponding properties ;
- For example, define data in data , define methods in methods , define computed properties in computed , monitor property changes in watch , and also include life cycle hooks ;
But this code has a big drawback:
- When we implement a certain function , the code logic corresponding to this function will be split into various attributes;
- When our components become larger and more complex , the list of logical concerns will grow, and the logic of the same function will be split ;
- Especially for those who did not write these components in the first place , the code of this component is difficult to read and understand (other people who read components);
For the same logical concern, it is necessary to continuously jump to the corresponding code block, jumping around in data, methods, and watch.
12.1. Understanding the Composition API
It would be even better if we could collect code related to the same logical concern .
This is what the Composition API wants to do, and what it can help us do.
- In order to start using the Composition API, we need to have a place where we can actually use it (write code) ;
- In the Vue component, this location is the setup function ;
- It's just that this option is so powerful that we can use it to replace most of the other options written before ;
- Such as methods, computed, watch, data, life cycle, etc.;
12.2.Parameters of setup function
- The first parameter: props
- The second parameter: context
- For the type of props defined , we are still the same as the previous rules, defined in the props option ;
- And it is still possible to use the properties in props normally in the template , such as message;
- If we want to use props in the setup function , then we can’t get it through this (I’ll talk about why later);
- Because props are directly passed as parameters to the setup function , we can use them directly through parameters ;
- attrs : all non-prop attributes;
- slots : the slots passed by the parent component (this will work when returning from the rendering function, which will be discussed later);
- emit : emit is used when we need to emit events inside our components (because we cannot access this, we cannot emit events through this.$emit);
12.3. The return value of the setup function
Since setup is a function, it can also have a return value . What is its return value used for?
- The return value of setup can be used in the template template ;
- That is to say, we can replace the data option with the return value of setup ;
Even we can return an execution function instead of the method defined in methods :
<template>
<div>
<h2>{
{count}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
export default {
setup() {
let count = 100
const increment= () => {
count++
}
const decrement= () => {
count++
}
return {
count,
increment,
decrement
}
}
}
</script>
Return the increment and decrement in the setup.
- The answer is no ;
- This is because for a defined variable , by default, Vue does not track its changes to cause responsive operations on the interface ;
12.4.Reactive API
If we want to provide reactive features for the data defined in setup, then we can use the reactive function :
- Only complex types of data can be defined;
- And need to use import {reactive} from 'vue' import ;
-
import {reactive} from 'vue' export default { setup() { const account = reactive({ username: "username", password: "123456" }) const clickAccount = () => { account.username = "jjj" } return { account, clickAccount } } }
Responsive operations can be performed when the data changes.
12.5. Ref API (important)
The reactive API has restrictions on the type passed in , it requires us to pass in an object and an array type :
- If we pass in a basic data type (String, Number, Boolean), a warning will be reported ;
At this time, Vue3 provides us with another API: ref API. In addition to defining simple type data, ref API can also define complex type data
- ref will return a mutable responsive object , which maintains its internal value as a responsive reference , which is the source of the ref name ;
- Its internal value is maintained in the value attribute of ref ;
const message = ref("Hello World")
- When introducing the value of ref in the template , Vue will automatically help us unpack it, so we don't need to use ref.value in the template;
- But inside the setup function , it is still a ref reference , so when operating on it, we still need to use the ref.value method ;
12.6. Understanding readonly
- Vue3 provides us with the readonly method ;
- readonly will return the read-only proxy of the original object (that is, it is still a Proxy, which is a proxy whose set method is hijacked and cannot be modified );
- Type 1: common object ;
- Type 2: the object returned by reactive ;
- Type 3: object of ref ;
12.6.1. Use of readonly
During the use of readonly, there are the following rules:
- The objects returned by readonly are not allowed to be modified ;
- But the original object processed by readonly is allowed to be modified;
✓ For example, const info = readonly(obj), the info object is not allowed to be modified ;
✓ When obj is modified , the info object returned by readonly will also be modified;
✓ But we cannot modify the object info returned by readonly ;
So what is the use of this readonly?
- When we pass data to other components, we often want other components to use the content we pass, but when they are not allowed to modify, we can use readonly;
12.7. API for Reactive judgment (understand)
- Check if the object is a proxy created reactively or readonly .
- Check if the object is a reactive proxy created by reactive :
- If the proxy is built by readonly , but wraps another proxy created by reactive , it will also return true;
- Checks if the object is a read-only proxy created by readonly .
- Returns the original object for reactive or readonly proxies ( keeping a persistent reference to the original object is not recommended. Use with caution).
- Creates a reactive proxy that tracks the responsiveness of its own properties, but does not perform deep reactive transformations of nested objects (deep or native objects).
- Create a proxy that makes its own property read-only, but does not perform deep read-only conversion of nested objects (deep layers are still readable and writable).
12.8.toRefs (understand)
If we use the ES6 deconstruction syntax to destructure the object returned by reactive to obtain the value , then whether it is to modify the variable after the structure or modify the state object returned by reactive , the data will no longer be responsive :
So is there a way to make our deconstructed properties responsive ?
- Vue provides us with a toRefs function , which can convert the attributes in the object returned by reactive into ref ;
- Then the name and age that we structure again are both ref ;
toRefs is equivalent to converting multiple to ref.
12.9.toRef (understand)
12.10.ref Other APIs
◼ unref
◼ If we want to get the value in a ref reference , we can also use the unref method :
- If the parameter is a ref , returns the internal value , otherwise returns the parameter itself ;
- This is the syntactic sugar function of val = isRef(val) ? val.value : val ;
- Determines whether the value is a ref object .
- Create a shallow ref object ;
- Manually triggering the side effects associated with shallowRef :
12.11.computed computed property
- In the previous Options API, we used the computed option to complete;
- In the Composition API, we can use the computed method in the setup function to write a computed property;
How to use computed?
- Method 1 : Receive a getter function and return an unchanged ref object for the value returned by the getter function;
-
<template> <div> <h2>{ {fullName}}</h2> </div> </template> <script> import {reactive, ref} from 'vue' import {toRefs, toRef, computed} from 'vue' export default { setup() { const names = reactive({ firstName:"aa", lastName:"bb" }) const fullName = computed(()=> { return names.firstName + " " +names.lastName }) return { names, fullName } } } </script>
- Method 2 : Receive an object with get and set , and return a mutable (readable and writable) ref object;
const fullName = computed({
get:()=>{
return names.firstName + " " +names.lastName
},
set: ()=> {
const tempNames = newValue.split(" ")
names.firstName = tempNames[0]
names.lastName = tempNames[1]
}
})
12.12. Use of ref in setup
In Chapter 11.2, if you want to get the native DOM, you can write ref in the element, and use this.$refs() in the method to get it. How to use ref to get the element or component in the setup?
- We only need to define a ref object and bind it to the ref attribute of an element or component;
<template>
<div>
<h2 ref="titleRef">我就是标题</h2>
<button @click="clickTitle">点击获取</button>
</div>
</template>
<script>
import {reactive, ref} from 'vue'
export default {
setup() {
const titleRef = ref()
const clickTitle = ()=> {
console.log(titleRef.value);
}
return {
titleRef,
clickTitle
}
}
}
</script>
12.13. Life cycle in setup function
We said earlier that setup can be used to replace data, methods, computed, etc. options, as well as life cycle hooks .
- Lifecycle hooks can be registered using directly imported onX functions;
Because setup runs around the beforeCreate and created lifecycle hooks , there is no need to define them explicitly , in other words, any code written in these hooks should be written directly in the setup function .
<template>
<div>
</div>
</template>
<script>
export default {
setup() {
onMounted(() => {
console.log("onMounted")
})
onUpdated(() => {
console.log("onUpdated")
})
onUnmounted(() => {
console.log("onUnmounted")
})
}
}
</script>
12.14. Provide function and Inject function
We have also learned about Provide and Inject before, and the Composition API can also replace the previous Provide and Inject options. But these shared content are managed more with vuex or pinia.
12.14.1.provide function
- Each Property can be defined through the provide method;
- name: the property name provided;
- value: the property value provided;
let counter = 100
let info = {
name: "zzz",
age: 18
}
provide("counter", counter)
provide("info", info)
12.14.2. Inject function
- The required content can be injected through inject ;
- The name of the property to be injected;
- default value;
12.15. watch listens to data
In the previous Options API, we can use the watch option to listen to data changes in data or props , and perform certain operations when the data changes.
- watchEffect: a dependency for automatically collecting responsive data;
- watch: You need to manually specify the data source to listen to;
12.15.1. Use of watch
- Watch needs to listen to a specific data source and execute its callback function;
- It is lazy by default, the callback will only be executed when the source being listened to changes;
const name = ref("zzz")
watch(name, (newValue, oldValue)=> {
console.log(newValue, oldValue);
})
Listen to multiple data sources
Listeners can also listen to multiple sources simultaneously using an array:
const name = ref("zzz")
const age = ref(18)
watch([name,age], (newValue, oldValue) => {
console.log(newValue, oldValue);
})
12.15.2.watch options
If we want to listen to a deep listening, we still need to set deep to true:
- You can also pass in immediate to execute immediately;
const name = ref("zzz")
const age = ref(18)
watch([name,age], (newValue, oldValue) => {
console.log(newValue, oldValue);
}, {
immediate: true,
deep: true
})
12.15.3.watchEffect
- First, the function passed in by watchEffect will be executed once immediately, and dependencies will be collected during execution;
- Secondly, only when the collected dependencies change, the function passed in by watchEffect will be executed again;
12.15.4.watchEffect stops listening
If in some cases, we want to stop listening, at this time we can get the return value function of watchEffect , just call this function .
12.16. script setup syntax
- Less boilerplate content, more concise code;
- Ability to declare props and throw events using pure Typescript;
- better runtime performance;
- Better IDE type inference performance;
<script setup>
console.log("setup")
</script>
The code inside will be compiled into the content of the component setup() function:
- This means that unlike normal <script> which is only executed once when the component is first introduced;
- The code in <script setup> will be executed every time a component instance is created.
12.16.1. Top-level bindings are exposed to templates
Responsive data needs to be created through ref and reactive.
12.16.2. Imported components are used directly
Values in the <script setup> scope can also be used directly as tag names for custom components:
12.16.3.defineProps()和defineEmits()
To get full type inference support when declaring props and emits options, we can use the defineProps and defineEmits APIs, which will automatically be available in <script setup>:
<template>
<div>
<h2>showInfo: {
{name}} - {
{age}}</h2>
<button @click="changeAge">修改Age</button>
</div>
</template>
<script setup>
const props = defineProps({
name: {
type: String,
default: ""
},
age: {
type: Number,
default: 0
}
})
const emit = defineEmits(["changeAge"])
function changeAge() {
emit("changeAge", 200)
}
</script>
So with defineProps and defineEmits, you can receive props and emit events.
12.16.4. defineExpose()
Components using <script setup> are disabled by default:
- The public instance of the component obtained through the template ref or $parent chain will not expose any bindings declared in <script setup>;
Use the defineExpose compiler macro to explicitly specify the property to be exposed in the <script setup> component: