为学习Vue基础知识,我动手操作通关了Vue演练场,该演练场教程的目标是快速体验使用 Vue 是什么感受,设置偏好时我选的是选项式 + 单文件组件。以下是我结合深入指南写的总结笔记,希望对Vue初学者有所帮助。
文章目录
九. 生命周期
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码。
最常用的是 mounted、updated 和 unmounted。
生命周期流程图
生命周期可用属性表
props、data、methods、computed、watch可用 | $el可用 | |
---|---|---|
beforeCreate | × | × |
created beforeMount | × | ✓ |
mounted beforeUpdate updated beforeUnmount unmounted | ✓ | ✓ |
服务端渲染方式对生命周期的影响
什么是服务端渲染?
服务器端渲染(Server-Side Rendering,简称SSR)是一种在Web开发领域中广泛应用的技术,与之相对的是传统的客户端渲染(Client-Side Rendering,CSR)。
服务端渲染与浏览器渲染有什么区别?
服务端渲染(SSR):在服务器端完成HTML页面的生成和渲染,然后将完整的HTML页面发送给客户端浏览器。服务器接收到用户请求后,从数据库或其他数据源获取数据,使用模板引擎(如EJS、Pug、Handlebars等)将数据与预定义的HTML模板结合,生成最终的HTML内容,并作为HTTP响应的一部分发送给客户端。
浏览器渲染(CSR):在客户端浏览器中通过JavaScript动态生成页面。前端通过AJAX等技术从后端获取数据,并根据数据动态生成DOM树,插入到HTML页面中。初次渲染时,通常会将原HTML中的数据标记替换为实际数据。
服务端渲染对浏览器的兼容性更强,对服务器的压力更大,对SEO关键词搜索机制更友好。
服务端渲染方式对Vue生命周期有什么影响?
由于服务端渲染直接输出静态HTML内容,不涉及DOM操作,所以服务端渲染时不会调用和DOM操作相关的生命周期,包括beforeMount
、mounted
、beforeUpdate
、updated
、beforeUnmount
和unmounted
。
具体生命周期介绍
1. beforeCreate
在组件实例初始化完成之后立即调用。
interface ComponentOptions {
beforeCreate?(this: ComponentPublicInstance): void
}
所有生命周期钩子函数的 this 上下文都会自动指向当前调用它的组件实例。
注意:
- 避免用箭头函数来定义生命周期钩子,因为如果这样的话你将无法在函数中通过 this 获取组件实例。
- 组合式 API 中的 setup() 钩子会在所有选项式 API 钩子之前调用,beforeCreate() 也不例外。
2. created
在组件实例处理完所有与状态相关的选项后调用。
当这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。
3. beforeMount
在组件被挂载之前调用。
4. mounted⭐️
在组件被挂载之后调用。
开始支持DOM操作,可以调用this.$el。
这个钩子通常用于执行需要访问组件所渲染的 DOM 树相关的副作用,或是在服务端渲染应用中用于确保 DOM 相关代码仅在客户端被调用。
5. beforeUpdate
在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。
这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。
注意:
最好不要在beforeUpdate
或updated
中修改组件状态,以免陷入无限更新循环。
6. updated⭐️
在组件因为一个响应式状态变更而更新其 DOM 树之后调用。
注意:
父组件的更新钩子将在其子组件的更新钩子之后调用。
这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。
7. beforeUnmount
在一个组件实例被卸载之前调用。
8. unmounted⭐️
在一个组件实例被卸载之后调用。
一个组件在以下情况下被视为已卸载:
- 其所有子组件都已经被卸载。
- 所有相关的响应式作用 (渲染作用以及 setup() 时创建的计算属性和侦听器) 都已经停止。
可以在这个钩子中手动清理一些副作用,例如清除计时器、注销注册过的DOM 事件监听器或者断开与服务器的连接。
还有一些不常用的钩子,包括errorCaptured、renderTracked、renderTriggered、activated、deactivated、serverPrefetch,有兴趣可看这里。
拓展:nextTick
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
nextTick
方法是Vue提供的一个全局 API,用于在下次 DOM 更新循环结束之后执行延迟回调。
function nextTick(callback?: () => void): Promise<void>
使用场景:
- 用于局部数据更新
nextTick()
用作组件中局部数据更新后DOM更新结束后的操作,全局的数据更新可以用updated
生命周期函数。 - 用于在连续逻辑中加入等待DOM更新的条件
在状态改变后立即使用nextTick() ,以等待 DOM 更新完成,确保在执行callback
之前,DOM已经完成了所有更改。
使用方法:
- await 返回的 Promise
import { nextTick } from 'vue'
export default {
data() {
return {
count: 0
}
},
methods: {
async increment() {
this.count++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
}
}
// 或
nextTick().then(()=>{
console.log(document.getElementById('counter').textContent) // 1
});
- 传递一个回调函数作为参数
import { nextTick } from 'vue'
nextTick(()=>{
console.log('已更新的DOM元素');
});
比全局版本的 nextTick()
更常见的用法是使用绑定在实例上的函数:this.$nextTick()
。
interface ComponentPublicInstance {
$nextTick(callback?: (this: ComponentPublicInstance) => void): Promise<void>
}
可以看到,callback的参数里多了一个this。两个nextTick唯一区别就是组件传递给 this.$nextTick() 的回调函数会带上 this 上下文,其绑定了当前组件实例。
await this.$nextTick()
和this.$nextTick(callback)
有什么区别?
没有搜到具体的讲解,但搜到了一个博主debug的分析:这里。还是不是很理解,尝试写一下,说得不对还请指正。
请看下面这个例子: 需要实现一个需求,一个小区业务详情页面,在左侧菜单栏切换了小区后,详情页跟着切换。watch: { async neighNo(newVal) { if (newVal) { this.$nextTick(() => { this.$router.replace(`/base/business/${newVal}`); }); } } }
当前页会由于菜单栏小区切换而刷新,那么此时当前的页面实例将要销毁,而this应该绑定在了这个要销毁的实例上。然后又切换了路由,重新创建了页面实例,this在这个过程中失去了正确的上下文,从而导致data失去了响应性。
换成下面这样用await的写法,this会根据语法作用域来确定绑定的上下文,每次都会绑定在对应的页面实例上,从而工作正常。await this.$nextTick() this.$router.replace(`/base/business/${newVal}`)
总结:
await this.$nextTick()
是更好的写法。
十. 模板引用
之前在Vue演练场基础知识(一)声明式渲染+Attribute绑定+事件监听中写到过一部分模板和指令的内容,这里再简单写一下。
文本插值
<span>Message: {{ msg }}</span>
原始 HTML
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html
指令:
<script setup>
const rawHtml = "<span style='color: red'>This should be red.</span>";
<script>
<template>
<span v-html="rawHtml"></span>
<template>
渲染的结果是在HTML中插入:
<span><span style="color: red">This should be red.</span></span>
v-html
指令的参数仅限纯 HTML,数据绑定将会被忽略。
注意:在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用 v-html,并且永远不要使用用户提供的 HTML 内容。
Attribute 绑定
使用v-bind
指令:
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>
如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。
同名简写(仅支持 3.4 版本及以上)
<div :id></div>
等同于:
<div :id="id"></div>
布尔型 Attribute
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled 就是最常见的例子之一。
如果绑定的值是 真值 或 null,那么元素会包含这个 disabled attribute,否则不包含。
<button :disabled="isButtonDisabled">Button</button>
动态绑定多个值
<div v-bind="objectOfAttrs"></div>
相当于绑定了objectOfAttrs
中的所有属性。
调用函数
<time :title="toTitleDate(date)" :datetime="date">
{{ formatDate(date) }}
</time>
注意:绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
指令 Directives
指令是带有 v- 前缀的特殊 attribute。Vue 提供了许多内置指令。一个指令的任务是在其表达式的值变化时响应式地更新 DOM。
<div v-bind="objectOfAttrs"></div>
动态参数
<a :[attributeName]="url"> ... </a>
// attributeName = href时等同于
<a :href="url"> ... </a>
<a @[eventName]="doSomething"> ... </a>
// eventName = focus时等同于
<a @focus="doSomething"> ... </a>
注意:
- 动态参数中表达式的值应当是一个字符串,或者是 null。特殊值 null 意为显式移除该绑定。
- 参数中不要有空格或引号,编译器会警告。如果你需要传入一个复杂的动态参数,我们推荐使用计算属性。
<a :['foo' + bar]="value"> ... </a>
当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写。
<a :[someAttr]="value"> ... </a>
// 将会被转换为
<a :[someattr]="value"> ... </a>
修饰符 Modifiers
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。
例如 .prevent 修饰符会告知 v-on 指令对触发的事件调用 event.preventDefault():
<form @submit.prevent="onSubmit">...</form>