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

组件模板引用(ref)变化

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

组件模板引用(ref)变化

在Vue.js中,ref是一种特殊的属性,用于在模板中直接访问DOM元素或子组件实例。随着Vue 3的推出,ref的使用方式发生了一些重要变化,特别是在组合式API中的表现与选项式API有所不同。

ref的基本用法

在Vue 2中,ref主要用于获取DOM元素或组件实例:

<template>
  <div ref="myDiv">这是一个div元素</div>
  <child-component ref="child"></child-component>
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.myDiv) // 获取DOM元素
    console.log(this.$refs.child) // 获取子组件实例
  }
}
</script>

在Vue 3中,这种用法仍然有效,但在组合式API中,推荐使用新的ref函数:

<template>
  <div ref="myDiv">这是一个div元素</div>
</template>

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

const myDiv = ref(null)

onMounted(() => {
  console.log(myDiv.value) // 获取DOM元素
})
</script>

组合式API中的ref变化

Vue 3的组合式API引入了更灵活的ref系统。在<script setup>语法中,模板引用变得更加直观:

<template>
  <input ref="inputRef" />
  <button @click="focusInput">聚焦输入框</button>
</template>

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

const inputRef = ref(null)

function focusInput() {
  inputRef.value.focus()
}
</script>

当使用ref引用组件时,可以访问组件的公开属性:

<template>
  <child-component ref="childRef" />
</template>

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

const childRef = ref(null)

// 访问子组件的公开方法
childRef.value.someMethod()
</script>

ref与v-for的结合使用

ref用在v-for内部时,在Vue 2中会得到一个数组:

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

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, text: 'Item 1' },
        { id: 2, text: 'Item 2' }
      ]
    }
  },
  mounted() {
    console.log(this.$refs.itemRefs) // 数组形式
  }
}
</script>

在Vue 3中,这种行为有所改变。为了获取数组形式的引用,需要使用函数形式的ref

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

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

const items = ref([
  { id: 1, text: 'Item 1' },
  { id: 2, text: 'Item 2' }
])

const itemRefs = ref([])

function setItemRef(el) {
  if (el) {
    itemRefs.value.push(el)
  }
}

onMounted(() => {
  console.log(itemRefs.value) // 包含所有DOM元素的数组
})
</script>

动态ref名称

在某些情况下,可能需要动态指定ref的名称:

<template>
  <div :ref="dynamicRef">动态ref</div>
</template>

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

const dynamicRef = ref('myDynamicRef')

// 或者使用函数形式
function setDynamicRef(el) {
  // 处理引用
}
</script>

ref的类型推断

在TypeScript项目中,可以为ref添加类型注解以获得更好的类型支持:

<script setup lang="ts">
import { ref } from 'vue'
import type { MyComponent } from './MyComponent.vue'

const myComponentRef = ref<InstanceType<typeof MyComponent>>()
const inputRef = ref<HTMLInputElement | null>(null)
</script>

ref与响应式对象

当需要将多个引用组织在一起时,可以使用响应式对象:

<template>
  <form ref="formRef">
    <input ref="inputRefs.username" />
    <input ref="inputRefs.password" />
  </form>
</template>

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

const formRef = ref(null)
const inputRefs = reactive({
  username: null,
  password: null
})
</script>

组件暴露的ref控制

在Vue 3中,组件可以通过defineExpose明确指定哪些内容可以被ref访问:

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

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

defineExpose({
  publicMethod
})
</script>

<!-- ParentComponent.vue -->
<template>
  <child-component ref="childRef" />
</template>

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

const childRef = ref(null)

// 只能访问到publicMethod,无法访问internalState
childRef.value.publicMethod()
</script>

ref的性能考虑

虽然ref很方便,但过度使用可能会影响性能,特别是在大型列表中。Vue的响应式系统需要跟踪这些引用,因此应该:

  1. 避免在不需要时创建大量ref
  2. 在组件卸载时清理不必要的引用
  3. 对于静态元素,考虑使用document.getElementById等原生方法
<script setup>
import { onUnmounted } from 'vue'

const elementRef = ref(null)

onUnmounted(() => {
  elementRef.value = null // 帮助垃圾回收
})
</script>

ref与nextTick

由于DOM更新是异步的,有时需要在nextTick后才能访问最新的ref

<template>
  <div v-if="show" ref="conditionalRef">条件渲染的内容</div>
  <button @click="toggle">切换</button>
</template>

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

const show = ref(false)
const conditionalRef = ref(null)

async function toggle() {
  show.value = !show.value
  await nextTick()
  console.log(conditionalRef.value) // 现在可以安全访问
}
</script>

ref的函数形式

除了字符串和ref变量,还可以使用函数形式设置引用:

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

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

let divElement = ref(null)
</script>

这种形式在动态组件或需要更精细控制引用时特别有用。

ref与Teleport

当使用<Teleport>时,ref的行为也需要注意:

<template>
  <Teleport to="body">
    <div ref="teleportedRef">被传送的内容</div>
  </Teleport>
</template>

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

const teleportedRef = ref(null)

onMounted(() => {
  console.log(teleportedRef.value) // 仍然可以正确引用
})
</script>

ref在渲染函数中的使用

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

import { h, ref } from 'vue'

const myRef = ref(null)

export default {
  setup() {
    return () => h('div', { ref: myRef }, '内容')
  }
}

或者在JSX中:

const myComponent = () => {
  const inputRef = ref(null)
  
  return <input ref={inputRef} />
}

ref与自定义指令

ref和自定义指令同时应用到一个元素时,执行顺序需要注意:

<template>
  <div v-my-directive ref="myRef"></div>
</template>

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

const myRef = ref(null)
const vMyDirective = {
  mounted(el) {
    console.log('指令挂载')
  }
}

onMounted(() => {
  console.log('ref可用:', myRef.value)
})
// 输出顺序: 指令挂载 -> ref可用
</script>

ref与KeepAlive

当组件被<KeepAlive>缓存时,ref的行为保持一致:

<template>
  <KeepAlive>
    <child-component v-if="show" ref="childRef" />
  </KeepAlive>
  <button @click="show = !show">切换</button>
</template>

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

const show = ref(true)
const childRef = ref(null)

// 切换时ref保持稳定
</script>

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

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

前端川

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