Composables vs. Renderless Components in Vue 3

A couple of weeks ago, I saw an example of a renderless component in React, then I thought does this also work in Vue? In this post, I’ll share what I found about my question.

几周前,我在 React 中看到了一个无渲染组件的示例,然后我就想,这在 Vue 中也能实现吗?在这篇文章中,我将与大家分享我在这个问题上的发现。

Turns out, we do have renderless components in Vue. To have a better understanding of renderless components, we need to know what scoped slots are. Let’s have a quick review of scoped slots:

事实证明,Vue 中确实存在无呈现组件。为了更好地理解无呈现组件,我们需要知道什么是作用域插槽。让我们快速回顾一下作用域插槽:

Scoped slots are simply a way of passing data from a child component to its parent component, so we can use that data for the content of the same child component’s slot. Let’s say you have a component called ChildComponent with this template:

作用域插槽只是一种将数据从子组件传递到父组件的方式,因此我们可以将这些数据用于同一子组件的插槽内容。比方说,你有一个名为 ChildComponent 的组件,并带有这个模板:

<template>
  <div>
    <slot :data='data' />
  </div>
</template>

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

const data = ref('dummy data')
</script>

Now we’re gonna use this ChildComponent inside another component (the parent component obviously):

现在我们要在另一个组件(显然是父组件)中使用这个 ChildComponent

<template>
  <h1>Some Heading</h1>
  <ChildComponent v-slot='slotProps'>
    The data inside the child component is accessible here: {{ slotProps.data }}
  </ChildComponent>
</template>

<script setup>
import ChildComponent from './ChildComponent.vue'
</script>

You might ask why we don’t move the data property to the parent component and make use of it. Remember that each component has its own responsibility and it may not be the parent component’s responsibility to create the data property, (probably do some computing on it) and then make use of it. Sometimes the parent component just needs the value of the data property which is now accessible through scoped slots.

你可能会问,为什么我们不把 data 属性移到父组件中并加以利用呢?请记住,每个组件都有自己的责任,创建 data 属性(可能需要对其进行一些运算)然后使用它可能不是父组件的责任。有时,父组件只需要 data 属性的值,现在可以通过作用域插槽访问该值。

Renderless Components (无渲染组件)

If you create a component that only contains some logic and doesn’t render anything by itself, you have a renderless component. Here is the same parent component we discussed above, with a few changes:

如果你创建的组件只包含一些逻辑,本身并不渲染任何东西,那么你就拥有了一个无渲染组件。下面是我们上面讨论过的父组件,并做了一些改动:

<template>
  <ChildComponent v-slot={ data }>
    The data inside the child component is accessible here: {{ data }}
  </ChildComponent>
</template>

<script setup>
import ChildComponent from './ChildComponent.vue'

const someFunction = ({ data }) => {
 // update the state according to 'data' property
}
</script>

First I got rid of the dummy heading element, and then added a function that in this case performs the responsibility that lies on the parent component’s shoulders. Also this time I used destructuring to access the properties of scoped slots.

首先,我去掉了虚拟的标题元素,然后添加了一个函数,在这种情况下,这个函数负责执行父组件肩负的职责。这次我还使用了重构来访问作用域插槽的属性。

This parent component has no visual elements of its own and only cares about what change it should apply to the state.