Conditional wrap component in Vue 3 pt.3
Alexander Nenashev
Posted on May 10, 2024
Previously we observed 2 options to create a conditional wrap component in Vue 3. Now it's time for the most complex one which exploits vnodes in a deeper way.
On the client side it's pretty ordinary with the wrapper provided in a wrapper
slot and the wrapped content in the default one. But we do some magic inside the wrap component:
- We collect all leaf vnodes in the wrapper slot's vnode tree
- We inject our content into the leaves like they'd support a default slot. For that we should set
shapeFlag
of the leaves asShapeFlags.ARRAY_CHILDREN | ShapeFlags.ELEMENT
.
import { ShapeFlags } from "@vue/shared";
import { cloneVNode} from "vue";
const findLeaves = (vnode, cb) =>
vnode.children?.length ?
vnode.children.forEach(vnode => findLeaves(vnode, cb)) :
cb(vnode)
;
export default function Wrap({isWrapped}, {slots}){
if (!isWrapped) return slots.default();
const wrapper = slots.wrapper().map(vnode => cloneVNode(vnode));
findLeaves({children: wrapper}, vnode => {
vnode.shapeFlag = ShapeFlags.ARRAY_CHILDREN | ShapeFlags.ELEMENT;
(vnode.children ??= []).push(...slots.default());
});
return wrapper;
}
Wrap.props = { isWrapped: Boolean };
Usage:
<wrap :is-wrapped>
<template #wrapper>
<div class="wrapper">
<div class="inner-wrapper"></div>
</div>
</template>
<p>
I'm wrapped
</p>
</wrap>
As you see the DX is pretty good, with full ability to define our wrapper with any nesting fully inside a template.
Now you have a choice to select a wrap component from 3 options or probably combine them into one ultimate component.
💖 💪 🙅 🚩
Alexander Nenashev
Posted on May 10, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.