- Start Date: 2024-10-24
- Target Major Version: 3.x
- Reference Issues: (fill in existing related issues, if any)
- Implementation PR: (leave this empty)
This RFC proposes the addition of a new directive, v-skip
. The v-skip
directive conditionally skips rendering the current element or component while preserving and promoting its child nodes to the parent level in the node hierarchy. This behavior differs from v-if
, which entirely removes the node and its subtree when the condition is false
.
<template>
<Tooltip v-skip="!disabled" content="The button is disabled because ...">
<button :disabled="disabled">Submit</button>
</Tooltip>
</template>
There are scenarios where developers need to conditionally remove an element or component without discarding its children. Currently, achieving this requires workarounds such as using userland components or switching to render functions, which can be verbose and less intuitive.
Related:
-
This issue discussed the need of a conditional wrapper. The workaround (bare basic, not robust) provided in one comments is:
<template> <component :is="tag" v-if="wrap"> <slot /> </component> <slot v-else /> </template> <script setup lang="ts"> interface Props { tag: string; wrap: boolean; } defineProps<Props>(); </script>
There was also a comment proposing a
v-wrap-if
directive:<div v-wrap-if="someCondition"> <span> This is a span that is sometimes wrapped up in a div. </span> </div>
-
This RFC propsed a built-in
fragment
component to make utilizing conditional wrappers easier.<template> <component :is="shouldWrap ? WrapperComponent : 'fragment'"> <img src="cat.jpg" alt="Cat" /> </component> </template>
-
https://dev.to/alexander-nenashev/creating-a-conditional-wrap-component-in-vue-3-18ml
This post series introduced a way to implement a conditional wrapper component which is quite cumbersome and relied on some knowledge of Vue's internals. The usage of the component is also not as concise as a directive.
The userland component is quite hard to be implemented correctly and when it comes to toggling a component and passing through its props, slots and event listeners, it becomes verbose to use as well. It becomes even harder if we want correct type support.
The v-skip
directive aims to provide a more concise and declarative approach to tackle this problem. It eliminates the need for userland components, eases the pain of passing props
or switching to render functions, and makes the code more readable and maintainable.
The v-skip
directive accepts an expression that evaluates to a boolean value.
<div v-skip="condition">
<p>Hello</p>
</div>
-
If
condition
is truthy, the current node is skipped, and its children are rendered in its place (render as if it's a fragment node) and produces:<p>Hello</p>
-
Otherwise, the node and its children are rendered normally and produces:
<div> <p>Hello</p> </div>
Let's say we have a Tooltip
component which is used like this:
<Tooltip>
<button :disabled="disabled">Submit</button>
<template #content>
<em>The button is disabled because ...</em>
</template>
</Tooltip>
Which renders:
<div>
<button>Submit</button>
<div class="tooltip">
<em>The button is disabled because ...</em>
</div>
</div>
We can conditionally skip the Tooltip
component like this:
<Tooltip v-skip="!disabled">
<button :disabled="disabled">Submit</button>
<template #content>
<em>The button is disabled because ...</em>
</template>
</Tooltip>
- If
disabled
is falsy (v-skip
evaluates totrue
), theTooltip
component is skipped, we directly render itsdefault
slot in its place and discard other slots, which produces:
<button>Submit</button>
- The children of the node with
v-skip
directive should be rerendered when the condition changes as the their parent may have provided contextual data (provide
, etc.). v-skip
cannot be used on<template>
and<slot>
.
- We may increase the Vue bundle size slightly due to the addition of the new directive.
Alternatives in userland is already covered in the motivation section.
Regarding to the naming, we can also consider alternatives like:
v-skip-if
v-wrap
v-wrap-if
v-unwrap
v-unwrap-if
The addition of v-skip
does not break existing code and is entirely opt-in.
Comprehensive documentation and examples will be provided to educate developers on proper usage.
We need built-in support from Vue Devtools and the official language support to recognize and correctly parse the v-skip
directive. We also need to add linting rules to eslint-plugin-vue
enforce proper usage of the directive.
N/A