Performance: Renderless components
Renderless components affect the rendering performance by minimizing unnecessary DOM nodes and reducing layout complexity.
<script setup> import { useSomeLogic } from './useSomeLogic' const state = useSomeLogic() </script> <template> <slot v-bind="state" /> </template>
<template>
<div>
<slot />
</div>
</template>
<script setup>
// logic here
</script>| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Wrapper div around slot | Adds 1 extra node per instance | Triggers 1 reflow per instance | Increases paint area | [X] Bad |
| Renderless component with scoped slots | No extra nodes added | No extra reflows | Minimal paint cost | [OK] Good |
renderless component in Vue?<template><slot :data="someData" /></template> uses a slot with a prop to pass data, which is the correct renderless pattern. Options A and D render UI elements, and C returns null but does not use slots.<RenderlessCounter v-slot="{ count, increment }">
<button @click="increment">Count: {{ count }}</button>
</RenderlessCounter>
count and increment via slot props, which the parent uses to render a button and handle clicks.increment, which increases count. The button text updates because it uses {{ count }}.<template>
<slot>Default</slot>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count++
}
</script>
count.value++, not count++.<template><slot :x="x" :y="y" /></template>
<script setup>
let x = 0
let y = 0
window.addEventListener('mousemove', e => {
x = e.clientX
y = e.clientY
})
</script> uses non-reactive plain variables, so position won't update reactively. <template><div>Mouse at {{ x }}, {{ y }}</div></template>
<script setup>
const x = ref(0)
const y = ref(0)
window.addEventListener('mousemove', e => {
x.value = e.clientX
y.value = e.clientY
})
</script> renders UI, so not renderless. <template><slot /></template>
<script setup>
import { reactive } from 'vue'
const position = reactive({ x: 0, y: 0 })
window.addEventListener('mousemove', e => {
position.x = e.clientX
position.y = e.clientY
})
</script> exposes slot without props, so no data passed. <template><slot :pos="position" /></template>
<script setup>
import { reactive } from 'vue'
const position = reactive({ x: 0, y: 0 })
window.addEventListener('mousemove', e => {
position.x = e.clientX
position.y = e.clientY
})
</script> exposes reactive position object via slot prop, clean and correct.Pass the test and this pattern moves unknown