阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 模板引用(ref)新特性

模板引用(ref)新特性

作者:陈川 阅读数:61061人阅读 分类: Vue.js

模板引用(ref)新特性

Vue.js 3.x 对模板引用功能进行了重大升级,提供了更灵活、更强大的 ref 使用方式。这些改进让开发者能够更直接地操作 DOM 元素和组件实例,同时保持了 Vue 的响应式特性。

ref 基础用法

在 Vue 3 中,ref 可以通过两种方式创建:

// 选项式 API
export default {
  data() {
    return {
      myRef: null
    }
  }
}

// 组合式 API
import { ref } from 'vue'

const myRef = ref(null)

在模板中使用时,只需在元素或组件上添加 ref 属性:

<template>
  <div ref="myRef">这是一个可引用的元素</div>
  <ChildComponent ref="childComponent" />
</template>

函数式 ref

Vue 3 引入了函数式 ref 的新写法,允许在 ref 绑定中使用函数:

<template>
  <div :ref="(el) => { divElement = el }">函数式引用</div>
</template>

<script setup>
import { ref } from 'vue'

const divElement = ref(null)
</script>

这种方式特别适合动态生成 ref 的场景,例如在 v-for 循环中:

<template>
  <div v-for="item in items" :key="item.id" :ref="setItemRef">
    {{ item.text }}
  </div>
</template>

<script setup>
import { ref, onBeforeUpdate } from 'vue'

const items = ref([/* ... */])
const itemRefs = ref([])

const setItemRef = el => {
  if (el) {
    itemRefs.value.push(el)
  }
}

// 在更新前清空 refs 数组
onBeforeUpdate(() => {
  itemRefs.value = []
})
</script>

ref 在组件上的使用

当 ref 用在组件上时,它指向的是组件实例:

<template>
  <ChildComponent ref="child" />
</template>

<script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'

const child = ref(null)

onMounted(() => {
  // 访问子组件的方法
  child.value.someMethod()
  
  // 访问子组件的属性
  console.log(child.value.someProperty)
})
</script>

使用 expose 控制组件暴露内容

Vue 3 新增了 expose 选项,可以精确控制组件对外暴露的内容:

<!-- ChildComponent.vue -->
<script setup>
import { ref } from 'vue'

const internalState = ref('私有数据')
const publicMethod = () => {
  console.log('这是公开方法')
}

// 只暴露 publicMethod
defineExpose({
  publicMethod
})
</script>

ref 与 TypeScript 的类型推断

Vue 3 对 TypeScript 的支持更加完善,可以为 ref 提供精确的类型提示:

import { ref } from 'vue'
import type { HTMLInputElement } from 'vue'

const inputRef = ref<HTMLInputElement | null>(null)

onMounted(() => {
  if (inputRef.value) {
    inputRef.value.focus() // 有正确的类型提示
  }
})

动态组件与 ref

动态组件上的 ref 行为有所不同,需要使用特殊处理:

<template>
  <component :is="currentComponent" ref="dynamicComponent" />
</template>

<script setup>
import { ref, shallowRef } from 'vue'
import CompA from './CompA.vue'
import CompB from './CompB.vue'

const currentComponent = shallowRef(CompA)
const dynamicComponent = ref(null)

// 切换组件时 ref 会自动更新
function toggleComponent() {
  currentComponent.value = currentComponent.value === CompA ? CompB : CompA
}
</script>

ref 与自定义指令

自定义指令也可以与 ref 配合使用:

<template>
  <div ref="target" v-custom-directive>指令目标</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const target = ref(null)

const vCustomDirective = {
  mounted(el) {
    console.log('指令挂载', el === target.value) // true
  }
}
</script>

ref 在渲染函数中的使用

在渲染函数中,ref 需要作为第二个参数传递:

import { h, ref } from 'vue'

const myRef = ref(null)

export default {
  setup() {
    return () => h('div', { ref: myRef }, '渲染函数中的引用')
  }
}

ref 与 Teleport 组件

Teleport 组件中的 ref 会指向传送后的 DOM 元素:

<template>
  <Teleport to="body">
    <div ref="teleported">会被传送到 body 的元素</div>
  </Teleport>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const teleported = ref(null)

onMounted(() => {
  console.log(teleported.value.parentNode) // body
})
</script>

ref 的性能考虑

对于大型列表,使用 ref 集合可能会影响性能。Vue 提供了 ref 数组的优化方案:

<template>
  <div v-for="i in 1000" :key="i" :ref="setRef">Item {{ i }}</div>
</template>

<script setup>
import { ref } from 'vue'

const refs = ref([])

function setRef(el) {
  if (el) {
    refs.value.push(el)
  }
}
</script>

ref 与 SSR 兼容性

在服务端渲染(SSR)环境中,ref 的行为需要注意:

<script setup>
import { ref, onMounted } from 'vue'

const clientOnlyRef = ref(null)

onMounted(() => {
  // 只在客户端执行
  console.log(clientOnlyRef.value)
})
</script>

ref 与 Suspense 组件

在 Suspense 环境中,ref 需要等待异步组件解析完成:

<template>
  <Suspense>
    <template #default>
      <AsyncComponent ref="asyncComp" />
    </template>
  </Suspense>
</template>

<script setup>
import { ref } from 'vue'
import AsyncComponent from './AsyncComponent.vue'

const asyncComp = ref(null)

// 需要等待 Suspense 解析完成
</script>

ref 的调试技巧

开发过程中可以方便地调试 ref:

import { ref } from 'vue'

const debugRef = ref(null)

// 添加 watcher 观察 ref 变化
watch(debugRef, (newVal) => {
  console.log('ref 值变化:', newVal)
}, { immediate: true })

ref 与第三方库集成

与第三方库集成时,ref 提供了便捷的接入点:

<template>
  <div ref="chartContainer"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'

const chartContainer = ref(null)

onMounted(() => {
  const chart = echarts.init(chartContainer.value)
  chart.setOption({/* ... */})
})
</script>

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌