无论是选项式 API 还是组合式 API 我们能可以通过 Ref 来拿到元素或组件的引用。
<template><div ref="divRef">Hello Vue3!</div><Child ref="childRef" /></template><script>import { ref, onMounted } from "vue";import Child from "./components/Child.vue";export default {components: {Child},setup() {const divRef = ref(null);const childRef = ref(null);console.log(divRef);console.log(divRef.value);console.log(childRef.value);return {divRef,childRef};}};</script><style lang="scss" scoped></style>
:::warning
⚠️ 注意
我们在代码中分别打印了divRef和divRef.value,从图中可以看到我们明明拿到了div元素对象。
这其实是因为我们在控制台点击三角形的时候会触发 getter 机制,此时我们的程序已经跑完,你再去 getter 肯定能拿到div元素。
:::
通常情况下,我们操作一个响应式数据是很正常的操作:
<template><div class="">This is Child component!</div><h1 ref="h1Ref">{{ state.title }}</h1><button @click="setTitle">Set title</button></template><script>import { ref, reactive, nextTick } from "vue";export default {setup() {const h1Ref = ref(null);const state = reactive({title: "This is title."});const setTitle = () => {state.title = "这是标题.";};return {h1Ref,state,setTitle};}};</script><style lang="scss" scoped></style>


以上代码,当你点击按钮的时候会更改state.title的值,相应的页面视图也会进行更新。
某些情况下,我们可能需要更改完数据后立马获取元素:
const setTitle = () => {state.title = "这是标题.";console.log(h1Ref.value);};
这时,你会发现获取的 DOM 引用的值还是 This is title.
这是怎么回事呢?下面是 Vue 文档的原话 :
当你更改响应式状态后,DOM 会自动更新。然而,你得注意 DOM 的更新并不是同步的。相反,Vue 将缓冲它们直到更新周期的 “下个时机” 以确保无论你进行了多少次状态更改,每个组件都只更新一次。
上面这段话的意思是:当数据状态更改后,DOM 是不会立马执行的,DOM 的更新是非同步的,Vue 会把 DOM 更新的任务缓存到一个队列中进行等待,当数据状态全部更改完成后再一次性的更新 DOM。
而如果你想拿到 DOM 更新后的内容,你可以使用 Vue 提供的nextTick()方法:
const setTitle = () => {state.title = "这是标题.";nextTick(() => {console.log(h1Ref.value);});};
nextTick()方法会在状态变更后立即执行,但是回调方法却是在 DOM 更新后才执行!!!
另外,nextTick()返回的是一个 Promise:
const setTitle = () => {state.title = "这是标题.";const res = nextTick();console.log(res);};

所以我们可以使用await进行中止等待:
const setTitle = async () => {state.title = "这是标题.";/* nextTick(() => {console.log(h1Ref.value);}); */// 也可以 await 执行await nextTick();console.log(h1Ref.value);};
