组件模板引用(ref)变化
组件模板引用(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的响应式系统需要跟踪这些引用,因此应该:
- 避免在不需要时创建大量
ref
- 在组件卸载时清理不必要的引用
- 对于静态元素,考虑使用
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