这两天review了一下Vue3的部分知识点,包括组件生命周期、ref 操作 DOM、nextTick 异步更新以及自定义组合式函数。这些都是 Vue3 开发中高频且核心的内容,整理成博客既方便自己后续回顾,也希望能帮到同阶段的同学~
一、Vue3 组件生命周期(Setup 语法糖版)
Vue3 的组合式API(尤其是 Setup 语法糖)重构了生命周期的写法,相比 Vue2的选项式API 更灵活(可以不将一个功能拆成三份:data,method,mounted);先梳理核心生命周期钩子的执行逻辑和使用场景。
1. 表格说明
| 钩子函数 | 执行时机 | 核心用途 |
|---|---|---|
| onBeforeMount | 组件挂载到 DOM 之前 | 挂载前的准备工作(不建议操作 DOM) |
| onMounted | 组件挂载到 DOM 之后 | 获取 DOM、初始化定时器 / 请求等 |
| onBeforeUpdate | 响应式数据更新,DOM 更新前 | 查看更新前的 DOM 状态 |
| onUpdated | 响应式数据更新,DOM 更新后 | 查看更新后的 DOM 状态 |
| onBeforeUnmount | 组件卸载之前 | 卸载前的清理准备 |
| onUnmounted | 组件卸载之后 | 清除定时器、解绑事件等 |
2. 代码示例与关键说明
TypeScript
<script setup lang="ts">
import {
onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted, ref
} from 'vue';
// 1. Setup函数的执行时机:在beforeCreate/created之间,所以这里的console先执行
console.log("setup 1");
// 模拟定时器(需在卸载时清除,避免内存泄漏)
const timer = setInterval(()=>{
console.log(new Date().toLocaleString());
},2000)
const number = ref(0)
// 组件挂载前:DOM尚未渲染,无法获取DOM元素
onBeforeMount(() => {
console.log("onBeforeMount");
})
// 组件挂载后:DOM已渲染,可安全获取DOM
onMounted(() => {
console.log("onMounted");
const dom = document.getElementById("dom")
console.log("挂载后获取DOM:", dom);
})
// 更新前:数据已改,DOM未更
onBeforeUpdate(() => {
console.log("onBeforeUpdate");
const result = document.querySelector(".number")?.innerHTML
console.log("更新前number:", result);
})
// 更新后:数据和DOM都已更新
onUpdated(() => {
console.log("onUpdated");
const result = document.querySelector(".number")?.innerHTML
console.log("更新后number:", result);
})
// 卸载前:可做最后的状态清理
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
})
// 卸载后:必须清除定时器/事件监听,避免内存泄漏
onUnmounted(() => {
console.log("onUnmounted");
clearInterval(timer)
})
console.log("setup 2");
</script>
<template>
<div id="dom">
<button @click="number++">点击增加</button>
<br>
number:<span class="number">{{ number }}</span>
</div>
</template>
关键注意点
- Setup 内部的代码会在组件实例创建初期执行(早于 onBeforeMount),因此直接在 Setup 中获取 DOM 会失败;
- 定时器、事件监听等 "副作用" 必须在 onUnmounted 中清理,否则组件卸载后仍会运行,造成内存泄漏;
- 生命周期钩子是按需导入的,减少无用代码打包体积
二、ref 获取 DOM 元素与组件通信
Vue3中放弃了Vue2的**this.$refs**,改用 ref 变量绑定DOM/组件,是操作DOM和组件通信的核心方式。
1. 场景一:获取普通 DOM 元素
通过ref:
TypeScript
<script setup lang="ts">
import { ref } from 'vue';
// 定义ref变量,与模板中的ref属性同名
const getdom = ref()
// 组件挂载后可通过getdom.value访问DOM
onMounted(() => {
// getdom.value拿到的是span这个dom元素本身
console.log(getdom.value.innerText); // 拿到DOM内容
})
</script>
<template>
<span ref="getdom">我是要获取的DOM</span>
</template>
2. 场景二:父组件调用子组件方法
子组件需通过**defineExpose**暴露方法 / 数据,父组件才能通过ref调用:
TypeScript
<!-- 子组件 -->
<script setup lang="ts">
import { ref } from 'vue';
const msg = ref("")
// 定义方法
function revMsg(data:string){
msg.value = data
}
// 暴露方法(Vue3 Setup默认封闭,需手动暴露)
defineExpose({
revMsg
})
</script>
<template>
<div>通过ref调用方法传递来的数据:{{ msg }}</div>
</template>
<!-- 父组件 -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import Child from 'xxxxx'; //导入子组件
const childRef = ref()
onMounted(() => {
// 调用子组件暴露的方法
childRef.value.revMsg("Hello 子组件!")
})
</script>
<template>
<Child ref="childRef" />
</template>
关键注意点
- ref 变量名需与模板中的**
ref**属性名完全一致; - 获取 DOM 必须在组件挂载后(onMounted),否则**
ref.value**为 undefined; - **
defineExpose**是Vue3 Setup语法糖的内置 API,无需导入,专门用于暴露组件内部属性 / 方法。
三、nextTick:异步获取更新后的 DOM
Vue的DOM更新是异步 的,修改响应式数据后立即获取 DOM,拿到的是更新前的状态,nextTick就是解决这个问题的核心方案。
1. 核心原理
nextTick的回调函数会 "插队" 到 DOM 更新完成后的第一个时机执行,确保获取最新的DOM 状态。
2. 代码示例
TypeScript
<script setup lang="ts">
import { nextTick, ref } from 'vue';
const number = ref(0)
const getdom = ref()
function add_number(){
number.value++
// 立即获取:拿到的是更新前的DOM
console.log('更新前:', getdom.value.innerText);
// nextTick回调:DOM更新后执行
nextTick(()=>{
console.log('更新后:', getdom.value.innerText);
})
}
</script>
<template>
<button @click="add_number">点击++</button>
数字:<span ref="getdom">{{ number }}</span>
</template>
执行结果
点击按钮后控制台输出:
XML
更新前:0
更新后:1
常见适用场景
- 修改数据后立即获取 DOM 尺寸 / 内容(如弹窗显示后获取宽高);
- 表单提交后刷新 DOM 状态;
- 批量修改数据后,统一获取最新 DOM。
四、自定义组合式函数
Vue3 的组合式 API 允许我们封装可复用的逻辑,抽离成 "组合式函数",解决 Options API 中 "逻辑复用难" 的问题。
1. 核心需求场景
组合式函数是Vue3最核心的逻辑复用方案,专门解决 Vue2 中难以复用的副作用逻辑、冗余代码问题 ,真实项目中高频使用场景:把组件中重复、独立的逻辑抽离出来,封装成独立的函数,就是组合式函数。
2.示例------封装组合式函数:useCounter.ts
TypeScript
import { ref } from "vue"
// 封装计数逻辑,支持初始化值
export function useCounter(initValue:number = 0){
// 响应式数据
const count = ref(initValue)
// 方法:加1
function increment(){
count.value++
}
// 方法:减1
function decrement(){
count.value--
}
// 方法:重置
function reset(){
count.value = initValue
}
// 返回需要暴露的属性/方法
return {
count,increment,decrement,reset
}
}
3. 组件中使用组合式函数
TypeScript
<script setup lang="ts">
// 导入自定义组合式函数
import { useCounter } from '@/use_tools/useCounter';
// 调用函数,初始化值为10,解构出属性和方法
const {count,reset,increment,decrement} = useCounter(10)
</script>
<template>
<div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="reset">reset</button>
<span>{{ count }}</span>
</div>
</template>
Tip:
关于代码中的解构 的解释:对象解构赋值是ES6新增的语法糖,核心作用是:把对象里的多个属性一次性 "拆" 出来,赋值给同名变量,避免写重复的「对象。属性」代码。
TypeScript
const {count,reset,increment,decrement} = useCounter(10)
// 等价于
const counterResult = useCounter(10); // 先拿到返回的对象
const count = counterResult.count; // 拆出count
const reset = counterResult.reset; // 拆出reset
const increment = counterResult.increment; // 拆出increment
const decrement = counterResult.decrement; // 拆出decrement
组合式函数优势
- 逻辑复用:多个组件可直接调用,无需重复写代码;
- 逻辑解耦:将独立的业务逻辑抽离,组件只负责渲染和交互;
- 类型友好:TS 加持下,参数和返回值类型清晰,便于维护。
小结
围绕着组件的生命周期开始总结
- 生命周期:掌握执行时机,重点关注挂载后(DOM 操作)和卸载后(清理副作用);
- ref:操作 DOM / 组件通信的核心,记住**
defineExpose**暴露子组件方法; - nextTick:解决异步 DOM 更新的问题,确保获取最新 DOM 状态;
- 组合式函数:封装复用逻辑,是 Vue3 组合式 API 的核心优势。
好了,如果你也正在学习这一方面那就开始动手吧~