1. Component life cycle
1.1. Understanding the life cycle
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 ;
Lifecycle function:
- 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 ;
1.2. Life cycle process
1.3. Process analysis of life cycle
App/Home/Banner/ShowMessage
- Before creating a component instance
beforeCreate
- Create a component instance
created (important: 1. Send network request; 2. Event monitoring; 3. this.$watch())
- template template compilation
beforeMount
- Mount to virtual DOM, virtual DOM->real DOM->interface see h2/div
mounted (important: element has been mounted, get DOM, use DOM)
- Data update: message changes
beforeUpdate
- Generate a new VNode based on the latest data, generate a new virtual DOM -> real DOM
updated
- no longer use v-if="false"
beforeUnmount
- Remove the VNode previously mounted in the virtual DOM from the virtual DOM
unmounted (relatively important: recycling operation (cancel event listening))
- Destroy the component instance
1.4. Examples:
Home.vue
<template>
<h2>Home</h2>
</template>
<script>
export default {
beforeUnmount() {
console.log("home beforeUnmount")
},
unmounted() {
console.log("home unmounted")
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<h2>message: {
{message}}-{
{counter}}</h2>
<button @click="message = 'Hello World'">修改message</button>
<button @click="counter++">+1</button>
<div>
<button @click="isShowHome = !isShowHome">显示Home</button>
<home v-if="isShowHome"></home>
</div>
</template>
<script>
import Home from "./Home.vue"
export default {
components: {
Home
},
data() {
return {
message: "Hello App",
counter: 0,
isShowHome: true
}
},
// 1.组件被创建之前
beforeCreate() {
console.log("beforeCreate")
},
// 2.组件被创建完成
created() {
console.log("created")
console.log("1.发送网络请求, 请求数据")
console.log("2.监听eventbus事件")
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.数据发生改变
// 5.1. 准备更新DOM
beforeUpdate() {
console.log("beforeUpdate")
},
// 5.2. 更新DOM
updated() {
console.log("updated")
},
// 6.卸载VNode -> DOM元素
// 6.1.卸载之前
beforeUnmount() {
console.log("beforeUnmount")
},
// 6.2.DOM元素被卸载完成
unmounted() {
console.log("unmounted")
}
}
</script>
<style scoped>
</style>
2. ref gets the element component
2.1, the use of $refs
- In some cases, we want to directly get the element object or subcomponent instance in the component :
- We do not recommend DOM manipulation in Vue development ;
- At this time, we can bind a ref attribute to the element or component ;
- Component instances have a $refs property :
- It is an Object that holds all DOM elements and component instances registered with the ref attribute.
2.2, $parent and $root
- We can access the parent element through $parent .
- Implementation of HelloWorld.vue:
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.
2.3. Examples
banner.view
<template>
<div class="banner">
<h2>Banner</h2>
</div>
</template>
<script>
export default {
created() {
},
methods: {
bannerClick() {
console.log("bannerClick")
}
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div class="app">
<!--ref属性绑定值,然后下面我们就能轻易的获取-->
<h2 ref="title" class="title" :style="{ color: titleColor }">{
{ message }}</h2>
<button ref="btn" @click="changeTitle">修改title</button>
<banner ref="banner"/>
</div>
</template>
<script>
import Banner from "./Banner.vue"
export default {
components: {
Banner
},
data() {
return {
message: "Hello World",
titleColor: "red"
}
},
methods: {
changeTitle() {
// 1.不要主动的去获取DOM, 并且修改DOM内容
// this.message = "你好啊, 李银河!"
// this.titleColor = "blue"
// 2.获取h2/button元素
console.log(this.$refs.title)
console.log(this.$refs.btn)
// 3.获取banner组件: 组件实例
console.log(this.$refs.banner)
// 3.1.在父组件中可以主动的调用子组件的对象方法
this.$refs.banner.bannerClick()
// 3.2.获取banner组件实例, 获取banner中的元素
console.log(this.$refs.banner.$el)
// 3.3.如果banner template是多个根, 拿到的是第一个node节点
// 注意: 开发中不推荐一个组件的template中有多个根元素
// console.log(this.$refs.banner.$el.nextElementSibling)
// 4.组件实例还有两个属性(了解):
console.log(this.$parent) // 获取父组件
console.log(this.$root) // 获取根组件
}
}
}
</script>
<style scoped>
</style>
3. The use of dynamic components
3.1. Case of switching components
For example, we now want to implement a function:
Click a tab-bar to switch between different component displays;
We can implement this case through two different implementation ideas:
- Method 1: Use v-if to judge and display different components;
- Method 2: the method of dynamic components ;
3.2, v-if displays different components
We can first judge and display different components through v-if, which can be realized by using the knowledge we mentioned before:
3.3. Realization of dynamic components
Dynamic components are implemented using component components through a special attribute is :
What does the value of currentTab need to be?
- 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 ;
3.4. Value passing of dynamic components
If it is a dynamic component, can we pass values to them and listen to events?
- the same;
- It's just that we need to put attributes and listener events on the component for use;
3.5. Examples
About.vue
<template>
<div>
<h2>About组件</h2>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
Home.vue
<template>
<div>
<h2>Home组件: {
{ name }} - {
{ age }}</h2>
<button @click="homeBtnClick">homeBtn</button>
</div>
</template>
<script>
export default {
props: {
name: {
type: String,
default: ""
},
age: {
type: Number,
default: 0
}
},
emits: ["homeClick"],
methods: {
homeBtnClick() {
this.$emit("homeClick", "home")
}
}
}
</script>
<style scoped>
</style>
Category.vue
<template>
<div>
<h2>Category组件</h2>
</div>
</template>
<script>
export default {
}
</script>
<style scoped>
</style>
app.vue
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button :class="{ active: currentTab === item }"
@click="itemClick(item)">
{
{ item }}
</button>
</template>
</div>
<div class="view">
<!-- 1.第一种做法: v-if进行判断逻辑, 决定要显示哪一个组件 -->
<!-- <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> -->
<!-- 2.第二种做法: 动态组件 component -->
<!-- is中的组件需要来自两个地方: 1.全局注册的组件 2.局部注册的组件 -->
<!-- <component :is="tabs[currentIndex]"></component> -->
<component name="why"
:age="18"
@homeClick="homeClick"
:is="currentTab">
</component>
</div>
</div>
</template>
<script>
import Home from './views/Home.vue'
import About from './views/About.vue'
import Category from './views/Category.vue'
export default {
components: {
Home,
About,
Category
},
data() {
return {
tabs: ["home", "about", "category"],
// currentIndex: 0
currentTab: "home"
}
},
methods: {
itemClick(tab) {
this.currentTab = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
</script>
<style scoped>
.active {
color: red;
}
</style>
4. Keep-alive components
4.1. Understanding keep-alive
Let's first modify the About component in the previous case:
- A button is added to it, and the function can be incremented by clicking;
For example, if we set the counter to 10, can the state be maintained when switching to home and then back to about ?
- the answer is negative;
- This is because by default, after we switch components, the about 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 .
4.2, keep-alive attribute
keep-alive has some properties:
- 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 is reached, the instances in the cache component that have not been accessed recently will be destroyed;
The include and exclude props allow components to be cached conditionally:
- Both can be represented as comma-separated strings, regular expressions, or an array ;
- The match first checks the name option of the component itself ;
4.3, 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;
4.4. Examples
Home.vue
<template>
<div>
<h2>Home组件</h2>
<h2>当前计数: {
{ counter }}</h2>
<button @click="counter++">+1</button>
</div>
</template>
<script>
export default {
name: "home",
data() {
return {
counter: 0
}
},
created() {
console.log("home created")
},
unmounted() {
console.log("home unmounted")
},
// 对于保持keep-alive组件, 监听有没有进行切换
// keep-alive组件进入活跃状态
activated() {
console.log("home activated")
},
deactivated() {
console.log("home deactivated")
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button :class="{ active: currentTab === item }"
@click="itemClick(item)">
{
{ item }}
</button>
</template>
</div>
<div class="view">
<!-- include: 组件的名称来自于组件定义时name选项 -->
<keep-alive include="home,about">
<component :is="currentTab"></component>
</keep-alive>
</div>
</div>
</template>
<script>
import Home from './views/Home.vue'
import About from './views/About.vue'
import Category from './views/Category.vue'
export default {
components: {
Home,
About,
Category
},
data() {
return {
tabs: ["home", "about", "category"],
// currentIndex: 0
currentTab: "home"
}
},
methods: {
itemClick(tab) {
this.currentTab = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
</script>
<style scoped>
.active {
color: red;
}
</style>
5. The use of asynchronous components
5.1, Webpack code subpackage
The default packaging process:
- By default, in the process of building the entire component tree, because the components are directly dependent on modules , webpack will package the component modules together when packaging (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 ;
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, which returns a promise object
5.2. 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.
- defineAsyncComponent accepts two types of parameters:
- Type 1: Factory function, which needs to return a Promise object;
- Type 2: Accept an object type and configure the asynchronous function;
- The writing method of factory function type 1:
5.3, the second way of writing asynchronous components (understand)
5.4. Examples
app.vue
<template>
<div class="app">
<div class="tabs">
<template v-for="(item, index) in tabs" :key="item">
<button :class="{ active: currentTab === item }"
@click="itemClick(item)">
{
{ item }}
</button>
</template>
</div>
<div class="view">
<!-- include: 组件的名称来自于组件定义时name选项 -->
<keep-alive include="home,about">
<component :is="currentTab"></component>
</keep-alive>
</div>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import Home from './views/Home.vue'
import About from './views/About.vue'
// import Category from './views/Category.vue'
// const Category = import("./views/Category.vue") // 导出的是promise对象
const AsyncCategory = defineAsyncComponent(() => import("./views/Category.vue"))
export default {
components: {
Home,
About,
Category: AsyncCategory
},
data() {
return {
tabs: ["home", "about", "category"],
// currentIndex: 0
currentTab: "home"
}
},
methods: {
itemClick(tab) {
this.currentTab = tab
},
homeClick(payload) {
console.log("homeClick:", payload)
}
}
}
</script>
<style scoped>
.active {
color: red;
}
</style>
6. The v-model of the component
Earlier we can use v-model in input to complete two-way binding :
- 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 ;
If we encapsulate a component now , can v-model be used to complete these two functions at the same time when using this component elsewhere?
- 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 ;
6.1. Realization of component v-model
So, in order for our MyInput component to work properly, the <input> inside this component must:
- Bind its value attribute to a prop named modelValue ;
- When its input event is triggered, the new value is thrown through the custom update:modelValue event ;
The component code of MyInput.vue is as follows:
6.2. Binding multiple properties
We are now directly binding an attribute through v-model, what if we want to bind multiple attributes?
- That is, is it possible for us to use multiple v-models on one component ?
- We know that the v-model by default is actually an event that binds the modelValue attribute and @update:modelValue ;
- If we want to bind more, we can pass a parameter to v-model , then the name of this parameter is the name of our bound attribute;
Note: Here I am binding two properties
v-model:title is equivalent to doing two things:
- The title attribute is bound ;
- Listened to the @update:title event ;
6.3. Examples
Counter.view
<template>
<div>
<h2>Counter: {
{ modelValue }}</h2>
<button @click="changeCounter">修改counter</button>
</div>
</template>
<script>
export default {
props: {
modelValue: {
type: Number,
default: 0
}
},
emits: ["update:modelValue"],
methods: {
changeCounter() {
this.$emit("update:modelValue", 999)
}
}
}
</script>
<style scoped>
</style>
Counter2.vue
<template>
<div>
<h2>Counter: {
{ counter }}</h2>
<button @click="changeCounter">修改counter</button>
<!-- why绑定 -->
<hr>
<h2>why: {
{ why }}</h2>
<button @click="changeWhy">修改why的值</button>
</div>
</template>
<script>
export default {
props: {
counter: {
type: Number,
default: 0
},
why: {
type: String,
default: ""
}
},
emits: ["update:counter", "update:why"],
methods: {
changeCounter() {
this.$emit("update:counter", 999)
},
changeWhy() {
this.$emit("update:why", "kobe")
}
}
}
</script>
<style scoped>
</style>
app.vue
<template>
<div class="app">
<!-- 1.input v-model -->
<!-- <input v-model="message">
<input :value="message" @input="message = $event.target.value"> -->
<!-- 2.组件的v-model: 默认modelValue -->
<counter v-model="appCounter"></counter>
<counter :modelValue="appCounter" @update:modelValue="appCounter = $event"></counter>
<!-- 3.组件的v-model: 自定义名称counter -->
<!-- <counter2 v-model:counter="appCounter" v-model:why="appWhy"></counter2> -->
</div>
</template>
<script>
import Counter from './Counter.vue'
import Counter2 from './Counter2.vue'
export default {
components: {
Counter,
Counter2
},
data() {
return {
message: "Hello World",
appCounter: 100,
appWhy: "coderwhy"
}
}
}
</script>
<style scoped>
</style>
7. Component mixing into Mixin
7.1. Understanding Mixins
- At present, we are developing the entire Vue application in a componentized way, but sometimes the same code logic exists between components , and we hope to extract the same code logic .
- One way supported in both Vue2 and Vue3 is to use Mixin to do it :
- 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 ;
7.2. Basic usage of Mixin
7.3, Mixin merge rules
If the options in the Mixin object conflict with the options in the component object, how will Vue operate?
- Here it is divided into different situations for processing;
Case 1: If it is the return value object of the data function
- 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 ;
Case 2: If it is a life cycle hook function
- The hook functions of the life cycle will be merged into the array and will be called;
Case 3: Options whose values are objects , such as methods, components, and directives, will be merged into the same object.
- 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 ;
7.4. Global mixing into Mixin
If some options in the component are required by all components, then we can use the global mixin at this time:
- 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 ;
7.5. Examples
message-mixin.js
export default {
data() {
return {
message: "Hello World"
}
},
created() {
console.log("message:", this.message)
}
}
Home.vue
<template>
<h2>Home组件</h2>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
// options api
components: {
},
mixins: [messageMixin],
data() {
return {
homeNames: [ "abc", "cba" ]
}
},
methods: {
},
created() {
console.log("home created")
}
}
</script>
<style scoped>
</style>
About.vue
<template>
<h2>About组件</h2>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
mixins: [messageMixin]
}
</script>
<style scoped>
</style>
Category.vue
<template>
<h2>Category组件</h2>
</template>
<script>
import messageMixin from '../mixins/message-mixin'
export default {
mixins: [messageMixin]
}
</script>
<style scoped>
</style>
app.vue
<template>
<div>
<home></home>
<about></about>
<category></category>
</div>
</template>
<script>
import Home from './views/Home.vue'
import About from './views/About.vue'
import Category from './views/Category.vue'
export default {
components: {
Home,
About,
Category
}
}
</script>
<style scoped>
</style>