Vue3-09

组件生命周期

在 Vue2 中,我们可以通过在组件中定义一些特定的生命周期函数来实现在组件的不同阶段执行不同的操作,比如说在组件创建的时候执行一些操作,或者是在组件销毁的时候执行一些操作,这些操作都是通过生命周期函数来实现的。

在 Vue3 中,组件的生命周期函数和 Vue2 中的生命周期函数是一样的,但是在 Vue3 中,组件的生命周期函数都是以 on 开头的,比如说在 Vue2 中的 beforeCreate 生命周期函数,在 Vue3 中就变成了 onBeforeCreate,在 Vue2 中的 created 生命周期函数,在 Vue3 中就变成了 onCreated,其他的生命周期函数也是一样的。

  • 选项式 API 生命周期选项

  • 组合式 API 生命周期钩子

组件的生命周期函数

  • 创建期间的生命周期函数

    • onBeforeCreate:在组件实例创建之前执行,此时组件的数据观测和事件机制都未初始化。
    • onCreated:在组件实例创建之后执行,此时组件的数据观测和事件机制都已经初始化完成。
  • 挂载期间的生命周期函数

    • onBeforeMount:在组件挂载到 DOM 之前执行,此时组件的模板已经编译完成,但是还没有挂载到 DOM 中。
    • onMounted:在组件挂载到 DOM 之后执行,此时组件已经挂载到 DOM 中。
  • 更新期间的生命周期函数

    • onBeforeUpdate:在组件更新之前执行,此时组件的数据已经更新完成,但是还没有更新到 DOM 中。
    • onUpdated:在组件更新之后执行,此时组件的数据已经更新完成,DOM 也已经更新完成。
  • 卸载期间的生命周期函数

    • onBeforeUnmount:在组件卸载之前执行,此时组件还没有卸载,DOM 也还没有卸载。
    • onUnmounted:在组件卸载之后执行,此时组件已经卸载,DOM 也已经卸载。
  • 销毁期间的生命周期函数

    • onBeforeDestroy:在组件销毁之前执行,此时组件还没有销毁,DOM 也还没有销毁。
    • onDestroyed:在组件销毁之后执行,此时组件已经销毁,DOM 也已经销毁。

Options API e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<template>
<div>
<h1>{{ message }}</h1>
<button @click="changeMessage">改变 message</button>
</div>
</template>

<script>
export default {
name: 'App',
data() {
return {
message: 'Hello Vue3, I am BeforeCreate',
}
},
beforeCreate() {
console.log('beforeCreate', this.message)
},
created() {
console.log('created', this.message)
},
beforeMount() {
console.log('beforeMount', this.message)
},
mounted() {
console.log('mounted', this.message)
},
beforeUpdate() {
console.log('beforeUpdate', this.message)
},
updated() {
console.log('updated', this.message)
},
beforeUnmount() {
console.log('beforeUnmount', this.message)
},
unmounted() {
console.log('unmounted', this.message)
},
beforeDestroy() {
console.log('beforeDestroy', this.message)
},
destroyed() {
console.log('destroyed', this.message)
},
methods: {
changeMessage() {
this.message = 'Hello Vue3, I am updated'
},
},
}
</script>

Composition API e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<template>
<div>
<h1>{{ message }}</h1>
<button @click="changeMessage">改变 message</button>
</div>
</template>

<script>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'

export default {
name: 'App',
setup() {
const message = ref('Hello Vue3, I am BeforeCreate')

onBeforeMount(() => {
console.log('beforeMount', message.value)
})
onMounted(() => {
console.log('mounted', message.value)
})
onBeforeUpdate(() => {
console.log('beforeUpdate', message.value)
})
onUpdated(() => {
console.log('updated', message.value)
})
onBeforeUnmount(() => {
console.log('beforeUnmount', message.value)
})
onUnmounted(() => {
console.log('unmounted', message.value)
})

const changeMessage = () => {
message.value = 'Hello Vue3, I am updated'
}

return {
message,
changeMessage,
}
},
}
</script>

对比可以发现,Options API 和 Composition API 的生命周期函数的使用方式是不一样的,Options API 中的生命周期函数是在组件的 export default 中定义的,而 Composition API 中的生命周期函数是在 setup 函数中定义的。但是不管是 Options API 还是 Composition API,它们的生命周期函数的执行顺序都是一样的。

生命周期的应用

生命周期的应用场景,常见的有:

  • 在组件创建的时候发送网络请求

  • 在组件销毁的时候清除定时器

  • 在组件更新的时候做一些特殊的处理

ref 获取 DOM 元素

在 Vue2 中,我们可以通过给元素添加 ref 属性来获取 DOM 元素,在四个时期中,只有在组件挂载到 DOM 之后,我们才能通过 this.$refs 来获取 DOM 元素,比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>
<h1 ref="title">Hello Vue3</h1>
</div>
</template>

<script>
export default {
name: 'App',
mounted() {
console.log(this.$refs.title)
},
}
</script>

在 Vue3 中,我们也可以通过给元素添加 ref 属性来获取 DOM 元素,但是在 Vue3 中,我们还可以通过 ref 函数来获取 DOM 元素,比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div>
<h1 ref="title">Hello Vue3</h1>
</div>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
name: 'App',
setup() {
const title = ref(null)

onMounted(() => {
console.log(title.value)
})

return {
title,
}
},
}
</script>

模拟网络请求

在 Vue2 中,我们可以在 created 生命周期函数中发送网络请求,比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
<h2>{{ item.title }}</h2>
<p>{{ item.content }}</p>
</li>
</ul>
</div>
</template>

<script>
export default {
data() {
return {
list: [],
}
},
created() {
this.list = [
{
id: 1,
title: '标题1',
content: '内容1',
},
{
id: 2,
title: '标题2',
content: '内容2',
},
{
id: 3,
title: '标题3',
content: '内容3',
},
]
}
}
</script>

分析代码,可以看到,我们选择了 created 生命周期函数来发送网络请求,因为在 created 生命周期函数中,组件的数据已经初始化完成,但是组件还没有挂载到 DOM 中,所以我们可以在 created 生命周期函数中发送网络请求,然后在网络请求成功之后,将数据保存到组件的数据中,这样就可以在组件挂载到 DOM 之后,直接从组件的数据中获取到数据,然后渲染到页面中。

created 阶段,我们拿到了数据,但数据还没有渲染到页面上,所以页面上是看不到数据的,这就会导致一个问题,就是在 created 阶段,我们无法获取到页面上的 DOM 元素,因为 DOM 元素还没有渲染到页面上。

先拿到数据,再渲染到页面上,这样的操作是有问题的,因为在 created 阶段,我们拿到的数据是一个空数组,这样的话,页面上就会出现一个空的列表,然后等到数据渲染到页面上之后,列表才会显示出来,这样的操作是不友好的。

在 Vue3 中,我们可以通过在 setup 函数中发送网络请求,然后将数据保存到 ref 中,这样的话,我们就可以在 setup 函数中直接使用数据,而不需要等到组件挂载到 DOM 之后才能使用数据,比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
<h2>{{ item.title }}</h2>
<p>{{ item.content }}</p>
</li>
</ul>
</div>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
setup() {
const list = ref([])

onMounted(() => {
setTimeout(() => {
list.value = [
{
id: 1,
title: '标题1',
content: '内容1',
},
{
id: 2,
title: '标题2',
content: '内容2',
},
{
id: 3,
title: '标题3',
content: '内容3',
},
]
}, 1000)
})

return {
list,
}
},
}
</script>

动态组件

动态组件是指在一个组件中动态的切换另一个组件,比如说在一个页面中,我们有一个登录组件和一个注册组件,我们可以通过点击按钮来切换登录组件和注册组件,这样的组件就是动态组件。

先看一个例子:

先创建 Login.vue 组件:

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<h1>登录组件</h1>
</div>
</template>

<script>
export default {
name: 'Login',
}
</script>

再创建 Register.vue 组件:

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<h1>注册组件</h1>
</div>
</template>

<script>
export default {
name: 'Register',
}
</script>

最后创建 App.vue 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<button @click="toggleComponent">切换组件</button>
<component :is="currentComponent"></component>
</div>
</template>

<script>
import Login from './Login.vue'
import Register from './Register.vue'

export default {
name: 'App',
components: {
Login,
Register,
},
data() {
return {
currentComponent: 'Login',
}
},
methods: {
toggleComponent() {
this.currentComponent = this.currentComponent === 'Login' ? 'Register' : 'Login'
},
},
}
</script>

点击按钮,可以看到组件的切换效果。