阿里云主机折上折
  • 微信号
Current Site:Index > Changes in component template refs

Changes in component template refs

Author:Chuan Chen 阅读数:28950人阅读 分类: Vue.js

Changes in Component Template References (ref)

In Vue.js, ref is a special attribute used to directly access DOM elements or child component instances in templates. With the introduction of Vue 3, the usage of ref has undergone some significant changes, particularly in how it behaves in the Composition API compared to the Options API.

Basic Usage of ref

In Vue 2, ref was primarily used to obtain DOM elements or component instances:

<template>
  <div ref="myDiv">This is a div element</div>
  <child-component ref="child"></child-component>
</template>

<script>
export default {
  mounted() {
    console.log(this.$refs.myDiv) // Get DOM element
    console.log(this.$refs.child) // Get child component instance
  }
}
</script>

In Vue 3, this usage remains valid, but in the Composition API, it's recommended to use the new ref function:

<template>
  <div ref="myDiv">This is a div element</div>
</template>

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

const myDiv = ref(null)

onMounted(() => {
  console.log(myDiv.value) // Get DOM element
})
</script>

Changes to ref in the Composition API

Vue 3's Composition API introduced a more flexible ref system. In the <script setup> syntax, template references become more intuitive:

<template>
  <input ref="inputRef" />
  <button @click="focusInput">Focus Input</button>
</template>

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

const inputRef = ref(null)

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

When using ref to reference a component, you can access the component's public properties:

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

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

const childRef = ref(null)

// Access the child component's public method
childRef.value.someMethod()
</script>

Combining ref with v-for

When ref is used inside v-for in Vue 2, it returns an array:

<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) // Array form
  }
}
</script>

In Vue 3, this behavior has changed. To get an array of references, you need to use a function-style 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) // Array containing all DOM elements
})
</script>

Dynamic ref Names

In some cases, you may need to dynamically specify the name of a ref:

<template>
  <div :ref="dynamicRef">Dynamic ref</div>
</template>

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

const dynamicRef = ref('myDynamicRef')

// Or use a function form
function setDynamicRef(el) {
  // Handle the reference
}
</script>

Type Inference for ref

In TypeScript projects, you can add type annotations to ref for better type support:

<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 with Reactive Objects

When you need to organize multiple references together, you can use a reactive object:

<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>

Controlling Exposed refs in Components

In Vue 3, components can use defineExpose to explicitly specify which content can be accessed via ref:

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

const internalState = ref('secret')
const publicMethod = () => {
  console.log('This is a public method')
}

defineExpose({
  publicMethod
})
</script>

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

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

const childRef = ref(null)

// Only publicMethod is accessible, internalState is not
childRef.value.publicMethod()
</script>

Performance Considerations for ref

While ref is convenient, overusing it may impact performance, especially in large lists. Vue's reactivity system needs to track these references, so you should:

  1. Avoid creating numerous refs when unnecessary
  2. Clean up unnecessary references when components unmount
  3. For static elements, consider using native methods like document.getElementById
<script setup>
import { onUnmounted } from 'vue'

const elementRef = ref(null)

onUnmounted(() => {
  elementRef.value = null // Aids garbage collection
})
</script>

ref and nextTick

Since DOM updates are asynchronous, sometimes you need to wait for nextTick to access the latest ref:

<template>
  <div v-if="show" ref="conditionalRef">Conditionally rendered content</div>
  <button @click="toggle">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) // Now safe to access
}
</script>

Function-style ref

In addition to string and ref variables, you can also use a function form to set references:

<template>
  <div :ref="el => { if (el) divElement = el }">Function-style ref</div>
</template>

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

let divElement = ref(null)
</script>

This form is particularly useful for dynamic components or when you need finer control over references.

ref with Teleport

When using <Teleport>, the behavior of ref also requires attention:

<template>
  <Teleport to="body">
    <div ref="teleportedRef">Teleported content</div>
  </Teleport>
</template>

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

const teleportedRef = ref(null)

onMounted(() => {
  console.log(teleportedRef.value) // Still correctly referenced
})
</script>

Using ref in Render Functions

In render functions, ref needs to be passed via the second parameter:

import { h, ref } from 'vue'

const myRef = ref(null)

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

Or in JSX:

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

ref with Custom Directives

When both ref and a custom directive are applied to an element, the execution order matters:

<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('Directive mounted')
  }
}

onMounted(() => {
  console.log('ref available:', myRef.value)
})
// Output order: Directive mounted -> ref available
</script>

ref with KeepAlive

When a component is cached by <KeepAlive>, ref behavior remains consistent:

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

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

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

// ref remains stable during toggling
</script>

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

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.