Vue3 :生命周期、DOM 操作与自定义组合式函数

这两天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 的核心优势。

好了,如果你也正在学习这一方面那就开始动手吧~

相关推荐
Asmewill1 天前
uv包管理命令
前端
发现一只大呆瓜1 天前
深入浅出 Tree Shaking:Rollup 是如何“摇”掉死代码的?
前端·性能优化·vite
weixin199701080161 天前
《转转商品详情页前端性能优化实战》
前端·性能优化
钮钴禄·爱因斯晨1 天前
他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!
java·开发语言·前端·javascript·css·html
Watermelo6171 天前
理解 JavaScript 中的“ / ”:路径、资源与目录、nginx配置、请求、转义的那些事
前端·javascript·vue.js·chrome·nginx·正则表达式·seo
Beingchou1 天前
HTML头部元信息避坑指南大纲
前端·html
Hello--_--World1 天前
JS:this指向、bind、call、apply、知识点与相关面试题
开发语言·javascript·ecmascript
jserTang1 天前
手撕 Claude Code-4: TodoWrite 与任务系统
前端·javascript·后端
腹黑天蝎座1 天前
大屏开发必读:Scale/VW/Rem/流式/断点/混合方案全解析(附完整demo)
前端·javascript
jserTang1 天前
手撕 Claude Code-5:Subagent 与 Agent Teams
前端·javascript·后端