ref

ref

在 Vue 的组合式 API 中,ref() 函数是声明响应式状态的首选方法。它接受一个参数,并返回一个包含 .value 属性的响应式引用对象。

特点
  • 类型兼容性:ref() 可以包裹任何类型的值,包括原始类型和对象。
  • 响应式转换:非原始值将通过 reactive() 函数转为具有深层次响应式的对象。
  • 模版使用:在模板中,ref 对象会自动解包,无需使用 .value 属性。
  • JavaScript访问:在 JavaScript 代码中,访问或修改 ref 包裹的值时,需要通过 .value 属性。

创建响应式数据

1<script lang="ts" setup>
2import { ref } from 'vue'
3
4// 声明一个响应式的基本类型状态
5const num = ref(0)
6
7/**
8 * 在 JavaScript 中,通过 .value 属性访问和修改响应式状态
9 */
10const increment = () => {
11  num.value++
12}
13</script>
14
15<template>
16  <button @click="increment">{{ num }}</button>
17</template>
1<script lang="ts" setup>
2import { ref } from 'vue'
3
4// 声明一个响应式的引用类型状态
5const obj = ref({
6  num: 0,
7})
8
9const increment = () => {
10  obj.value.num++
11}
12</script>
13
14<template>
15  <button @click="increment">{{ obj.num }}</button>
16</template>

:::

获取 DOM 元素和组件实例

在 Vue 中,ref 属性用于在模板中注册 DOM 元素或子组件的引用,以便在 JavaScript 代码中访问它们。

  • DOM元素:当 ref 用于普通DOM标签时,它将引用 DOM 节点本身。

  • 组件实例:当 ref 用于组件标签时,它将引用组件的实例对象。

DOM元素

1<script lang="ts" setup>
2import { ref, nextTick, onMounted } from 'vue'
3
4// 当变量名和标签中ref的属性值一一对应时,就可以获取该DOM元素
5const box = ref()
6
7// 使用 nextTick 确保 DOM 更新完成后执行
8nextTick(() => {
9  console.log('nextTick', box.value) // 输出: <div>我是DOM元素</div>
10  console.log('nextTick', box.value.textContent) // 输出: 我是DOM元素
11})
12
13// 使用 onMounted 确保 DOM 完全挂载后执行
14onMounted(() => console.log('onMounted', box.value))
15</script>
16
17<template>
18  <div ref="box">我是DOM元素</div>
19</template>

组件实例

在 Vue 3 中,若需要在父组件中访问子组件的属性和方法,子组件必须使用 defineExpose 编译器宏来显式指定哪些属性和方法需要对外公开。注意 defineExpose 只在 <script setup> 标签中有效。这样才能确保父组件通过模板引用的方式能够访问并调用子组件的这些成员。

1<script lang="ts" setup>
2import { ref, onMounted } from 'vue'
3import ChildComponent from './ChildComponent.vue'
4
5// 定义一个响应式引用变量
6const childComponent = ref<InstanceType<typeof ChildComponent> | null>(null)
7
8// 使用 onMounted 确保组件完全挂载后执行
9onMounted(() => {
10  console.log('childComponent', childComponent.value)
11})
12
13// 定义一个 increment 方法来修改 childComponent 的 count
14const increment = () => {
15  if (childComponent.value) childComponent.value.increment()
16}
17</script>
18
19<template>
20  <ChildComponent ref="childComponent" />
21  <button @click="increment">Increment</button>
22</template>
1<script lang="ts" setup>
2import { ref, defineExpose } from 'vue'
3
4const num = ref(0)
5
6const increment = () => {
7  num.value++
8}
9
10defineExpose({
11  num,
12  increment,
13})
14</script>
15
16<template>
17  <div>
18    <p>我是子组件</p>
19    <button @click="increment">{{ num }}</button>
20  </div>
21</template>
22
23<style scoped>
24div {
25  border: 1px solid #ccc;
26  padding: 10px;
27}
28</style>

:::

shallowRef

triggerRef

customRef

isRef

isRef 是 Vue 3 的 Composition API 中的一个辅助函数,用于检查给定的值是否是一个 ref 对象。这对于在运行时确定一个变量是否被包装成响应式引用非常有用。

1import { ref, isRef } from 'vue'
2
3// 普通变量
4const age = 28
5
6// 使用 ref 创建的响应式变量
7const num = ref(0)
8
9// 检查 age 是否为 ref 对象
10console.log(isRef(age)) // 输出:false
11
12// 检查 num 是否为 ref 对象
13console.log(isRef(num)) // 输出:true

unref

unref 是 Vue3 的 Composition API 中的一个辅助函数,如果参数是 ref,则返回内部值,否则返回参数本身。这是 val = isRef(val) ? val.value : val 计算的一个语法糖。

对于非响应式对象,unref 直接返回对象本身,不会进行任何包装或解包操作。

1import { ref, unref } from 'vue'
2
3// 普通变量
4const age = 28
5// 响应式变量
6const num = ref(0)
7
8console.log(unref(age)) // 输出:28
9console.log(unref(num)) // 输出:0

toRef、toRefs

toReftoRefs 是 Vue3 的 Composition API 中的两个个辅助函数,它们用于将 reactive 对象的属性转换为 ref 对象,以保持响应性。

  • toRef 用于将单个属性转换为 ref

  • toRefs 用于将整个 reactive 对象的所有属性转换为 ref 对象。

1<script lang="ts" setup>
2import { reactive } from 'vue'
3
4const state = reactive({
5  count: 0,
6})
7
8// 解构会导致响应式丢失
9let { count } = state
10
11const increment = () => {
12  count++
13  console.log(count)
14}
15</script>
16
17<template>
18  <button @click="increment">{{ count }}</button>
19</template>
1<script lang="ts" setup>
2import { reactive, toRef } from 'vue'
3
4const state = reactive({
5  count: 0,
6})
7
8// 使用 toRef 保持响应性
9const count = toRef(state, 'count')
10
11const increment = () => {
12  count.value++
13}
14</script>
15
16<template>
17  <button @click="increment">{{ count }}</button>
18</template>
1<script lang="ts" setup>
2import { reactive, toRefs } from 'vue'
3
4const state = reactive({
5  count: 0,
6})
7
8// 使用 toRefs 转换所有属性为 ref 对象
9const { count } = toRefs(state)
10
11const increment = () => {
12  count.value++
13}
14</script>
15
16<template>
17  <button @click="increment">{{ count }}</button>
18</template>

:::