DEV Community

Nick Mousavi
Nick Mousavi

Posted on • Originally published at biomousavi.com

3 2 2 2 2

How to Access a Child Component’s Ref with multi-root node (Fragment) in Vue 3

Introduction

If you're used to Vue 2, you might remember that every component's template needed a single root element. In Vue 3, that's no longer necessary because of fragments. This means your components can now have multiple root elements without needing a wrapper.


<!-- Vue 2 -->
<template>
  <div> <!-- wrapper 😫 -->
    <h1>My Blog Post</h1>
    <ArticleComponent>{{ content }}</ArticleComponent>
  </div>
</template>

<!-- Vue 3 -->
<template>
  <h1>My Blog Post</h1>
  <ArticleComponent>{{ content }}</ArticleComponent>
</template>

Enter fullscreen mode Exit fullscreen mode

That's very similar to Fragment in React. However, Vue handles fragments behind the scenes. In fact, in Vue 3, you can think of the <template> tag as a fragment.

The ref() Problem

In Vue 2, we could easily set a ref on a child component, and it would refer to both the wrapper element and the component instance.

But in Vue 3, when there’s no wrapper element, what does the ref refer to? 🤔

If the child component uses the Options API or doesn't use <script setup>, the ref will point to the child component's this, giving the parent full access to its properties and methods.

What if we use <script setup> ?

Components using <script setup> are private by default. To expose properties, we need to use the defineExpose macro.

Access To Children's Element

  • This is what happen when you have wrapper (single root) element:
<!-- Child -->
<template>
  <div class="wrapper"> <!-- Root -->
    <h1>My Blog Post</h1>
    <ArticleComponent>{{ content }}</ArticleComponent>
  </div>
</template>



<!-- Parent -->
<script setup lang="ts">
const childRef = ref()

onMounted(()=>{
  console.log(childRef.value.$el); // <div class="wrapper"></div>  // [!code highlight]
})
</script>

<template>
  <Child ref="childRef" />
</template>
Enter fullscreen mode Exit fullscreen mode
  • And when you have more than one root:
<!-- Child -->
<template>
  <h1>My Blog Post</h1> <!-- Root 1 -->
  <ArticleComponent>{{ content }}</ArticleComponent> <!-- Root 2 -->
</template>



<!-- Parent -->
<script setup lang="ts">
const childRef = ref()

onMounted(()=>{
  console.log(childRef.value.$el); // #text  
})
</script>

<template>
  <Child ref="childRef" />
</template>
Enter fullscreen mode Exit fullscreen mode

Wait, what, what happened?

When we using Fragment(multiple nodes), Vue creates a text node that wraps our child component root nodes.

When using Fragments in Vue 3, Vue inserts an empty text node at the beginning of the component as a marker, which is why $el returns a #text node.

#text is like a reference point that Vue uses internally.

Also I should mention that you have still access to component instance (if you don't use <script setup> in child).

Solution

1) Use Single Root Like this
2) Use Template Refs + defineExpose

Using Template Refs + defineExpose

<!-- Child -->
<script setup lang="ts">
import { ref } from 'vue';

const h1Ref = ref()
const articleRef = ref()


defineExpose({
  h1Ref,
  articleRef
})
</script>

<template>
  <h1 ref="h1Ref">My Blog Post</h1> 
  <ArticleComponent ref="articleRef">{{ content }}</ArticleComponent> 
</template>



<!-- Parent -->
<script setup lang="ts">
const childRef = ref()

onMounted(()=>{
  console.log(childRef.value); 
  //  {h1Ref: RefImpl, articleRef: RefImpl}
})
</script>

<template>
  <Child ref="childRef" />
</template>
Enter fullscreen mode Exit fullscreen mode

Now you have access to your refs and all the things that you exposed by using defineExpose.

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs