Replies: 12 comments 46 replies
-
Interesting! I always go for creating a child component when I see repetition and while reading the problem I intuitively thought of solving it that way. But having multiple templates in order to reuse them inside a SFC looks amazing. I am thinking that this way of doing it, wouldn't require a new instance to be created right? Because if a new instance is created, then I guess there is no need to do that, we could just create a child component. So in terms of optimizations, having this way of reusing templates, would probably result in fewer component instances in userland. Thinking of logical concerns, this can align well I believe. Think for example: <script setup>
const count = ref(0)
function increase() {
count.value++
}
const user = ref({ name: 'Roland' })
function clear() {
user.value = { name: '' }
}
</script>
<template name="count">
Count is: {{ count }}
<button @click="increase">Increase</button>
</template>
<template name="user">
User name is: {{ user.name }}
<button @click="clear">Clear</button>
</template>
<template>
<div>
<template is="count"></template>
<hr />
<template is="user"></template>
</div>
</template> |
Beta Was this translation helpful? Give feedback.
-
🎉 Beta version released as |
Beta Was this translation helpful? Give feedback.
-
For this feature to really shine, you would have to give the possibility to pass context to that template. What do I mean by this? For example, you have to show 2-3 different templates inside a v-for, but you have to show them in different conditions and with different data structures. If we can have this, it means that we can also have the possibility to pass around this templates in order to be reused in other components. <script setup>
const users = ref([
{name: 'a', lastname: 1, onlyName: true},
{name: 'b', lastname: 2, onlyName: true},
{name: 'b', lastname: 3, onlyName: false},
]);
</script>
<v-template #userCard1 let-user>
User name is: {{ user.name }}
</v-template >
<v-template #userCard2 let-user>
User last name is: {{ user.lastName }}
</v-template>
<template>
<div v-for="user in users">
<v-template-outlet v-if="user.onlyName" :template="userCard1" :context="user"></v-template-outlet>
<v-template-outlet v-if="!user.onlyName" :template="userCard2" :context="user"></v-template-outlet>
</div>
</template> The idea is taken from Angular (ng-template, ng-template-outlet ) |
Beta Was this translation helpful? Give feedback.
-
Reference slots<script setup>
const users = ref([
{name: 'a', lastname: 1, onlyName: true},
{name: 'b', lastname: 2, onlyName: true},
{name: 'b', lastname: 3, onlyName: false},
]);
</script>
<template #userCard1="ctx">
User name is: {{ ctx.user.name }} {{ ctx.test }}
</template >
<template #userCard2="{ user, test }">
User name is: {{ user.name }} {{ test}}
</template>
<template>
<div v-for="user in users">
<template is="userCard1" :user="user" test="1"></template>
<template is="userCard2" :user="user" test="2"></template>
</div>
</template> |
Beta Was this translation helpful? Give feedback.
-
Template Block ProposalWhen It comes to this proposal. I'd like to say that even though JSX is just better to use I do support this proposal. I think there are two ways to implement this feature. I call this Open vs Closed blocks. The way that I want this proposal to be implemented is simple. We get two new components. A block component and a outlet component. The reason why people have made this proposal is for reasons related to using JSX. Weaknesses of JSX
Vue BlockThe Open BlockThis component has one prop
Creation of Open Block Vue block <script setup>
defineProps<{name:string}>()
</script>
// This template works like a normal template
// It is not picked up by vue
<template v-bind="{name}">
<slot/>
</template>
**Implementation of Vue block ** <script setup>
const foo = "foo"
</script>
<vue:block name="random-text">
// The content in here will not be rendered
unless the outlet is used
{{ foo }}
</vue:block>
Closed BlockThis component has two props
Creation of Vue Closed Block <script setup generic="T extends Record<string,unknown>">
defineProps<{name:string, context:T}>()
</script>
// This template works like a normal template
// It is not picked up by vue
<template v-bind="{name}">
<slot name="context" v-bind="{...context}" />
</template>
Implementation of Vue block <script setup>
const foo = "foo"
</script>
<vue:block name="random-text" #context="{foo}">
// The content in here will not be rendered
unless the outlet is used
{{ foo }}
This has no acesss to the state so we grab to through the context.
</vue:block>
Vue Block OutletA block outlet is the component that decides where a **Implementation of Block Outlet ** // look at the block example to find out where these props come from
<vue:block-outlet block="random-text" :context="{foo}">
ExamplesLike I said in previous examples. I talked about the difference between closed and open blocks. An open block automatically has access to the state of the app. A closed block doesn't. I will demonstrate how one would work vs the other. Let's say that this is my Vue component. <script setup>
const links = [
"Home",
"About",
"Contact",
"Services",
]
</script>
<template>
<nav>
// this is supposed to be a hamburger menu
<div>
<span></span>
<span></span>
<span></span>
</div>
// this is a set of links
<ul>
<li v-for="link of links">
{{link}}
</li>
</ul>
</nav>
</template>
My Vue component using open blocks <script setup>
const links = [
"Home",
"About",
"Contact",
"Services",
]
</script>
<template>
<nav>
// this is supposed to be a hamburger menu
<div>
<vue:block-outlet block="hamburger-menu" />
</div>
// this is a set of links
<ul>
<vue:block-outlet block="pages-links" />
</ul>
</nav>
</template>
<vue:block name="hamburger-menu">
<span></span>
<span></span>
<span></span>
</vue:block>
<vue:block name="pages-links">
<li v-for="link of links">
{{link}}
</li>
</vue:block>
**My Vue component using closed blocks ** <script setup>
const links = [
"Home",
"About",
"Contact",
"Services",
]
</script>
<template>
<nav>
// this is supposed to be a hamburger menu
<div>
<vue:block-outlet block="hamburger-menu" />
</div>
// this is a set of links
<ul>
<vue:block-outlet
block="pages-links"
:context="{links}"
/>
</ul>
</nav>
</template>
// These blocks have no acess to component state
<vue:block name="hamburger-menu">
<span></span>
<span></span>
<span></span>
</vue:block>
<vue:block name="pages-links" #context="{links}" >
<li v-for="link of links">
{{link}}
</li>
</vue:block>
|
Beta Was this translation helpful? Give feedback.
-
I think this is a great idea. How would we import these though? What I really want is to be able to use this approach to make multiple components that share state and or style. Like for instance, a togglable dropdown where you can import the dropdown and the toggle handle seperatley and place them however you want while still having the toggle be able to trivially manipulate the state of the dropdown. Example: <script setup>
const dropdown= ref(false)
</script>
<template name="handle">
<button @click="dropdown = !dropdown">{{dropdown ? "close" : "open"}}</button>
</template>
<template name="dropdown">
<div :class="dropdown" :class="{ open: dropdown }">
<slot/>
</div>
</template>
<style scoped>
.dropdown {
overflow: hidden;
padding: 12px;
max-height: 300px;
transition: .4s;
}
.dropdown:not(.open) {
max-height: 0px;
}
</style> and then import like this: <script setup>
import Shelf from "./components/shelf.vue"
</script>
<template>
<Shelf.handle/>
<div>Some other random stuff to prove a point</div>
<Shelf.dropdown>
<p>Some item 1</p>
<p>Some item 2</p>
<p>Some item 3</p>
<p>Some item 4</p>
</Shelf.dropdown>
</template> I feel like these kinds of DX wins could help give Vue an edge over things like Svelte in particular. Since svelte doesnt encapsulate its html in template tags but Vue does. Vue has a uniqe opportunity to extend this already existing pattern to compose multiple templates in ways that Svelte would require a bigger deviation from its established patterns to be able to match. |
Beta Was this translation helpful? Give feedback.
-
It very useful idea, it can maintain consistency and avoid jsx syntax. I would like to let them as <script setup>
import { ref } from 'vue'
const msg = ref('msg')
const count = ref(0)
function inc() {
count.value++
}
</script>
<part name="one">
<span>{{ message }}</span>
</part>
<part name="two">
<span @click="inc">{{ count }}</span>
</part>
<template>
<div>
<template v-part:one></template>
<template v-part:two></template>
</div>
</template> |
Beta Was this translation helpful? Give feedback.
-
After refactoring my lib Anu's code from TSX to SFC, This is the only missing feature in SFC over TSX. In TSX I was assigning the btn content to some variable and later wrap the loader based on the loading prop. This was super easy in TSX however with SFC and template tag there's no other way. Thanks @sxzz I don't have to refactor the button component from SFC to TSX again 🤣 I'm using named template from vue-macros for now. |
Beta Was this translation helpful? Give feedback.
-
Found an approach that requires no tooling support and you can use today: It also supports complete type checks. Give it a try, and I'd love to hear your feedback :) |
Beta Was this translation helpful? Give feedback.
-
@hminghe Thank you for bringing up this topic this is a problem that needs to be solved the optimal way of doing this is to use nested SFC's See an example here. It's called vite-plugin-vue-nested-sfc |
Beta Was this translation helpful? Give feedback.
-
Hi, guys! There's another approach coming out named "Vue Vine", and you can easily write multiple components, even just contain a bare template. Check out more details on Vue Vine docs. I hold the opinion that putting multiple templates (or we can say it is small fragments) is breaking the basic concept of "Single" file component. Over many years of iteration, the boundaries of SFC components are always first set in a single file, it doesn't make sense to create more rugged and winding approach to overpass its basic concepts, we should look for another way instead. So, I'm providing a more flexible organization approach for you to be able to write multiple components in 1 TS file, just like this: |
Beta Was this translation helpful? Give feedback.
-
with Svelte introducing this sort of reusable templating functionality in the form of snippet blocks I wonder if we can revisit this now, it'd be very nice to have this functionality in Vue. We've had several components where we've had to duplicate our templating for one reason or another, where having them as separate components doesn't seem worth doing, so having this would be a big plus. |
Beta Was this translation helpful? Give feedback.
-
What problem does this feature solve?
For example:
写组件的时候经常会有这个问题,然后又换成
tsx
写,但是tsx
的slot
和expose
是无法实现代码提示的。You often get this problem when you write components, and then switch to
tsx
, butslot
andexpose
don't allow for code hints.What does the proposed API look like?
Template to:
Beta Was this translation helpful? Give feedback.
All reactions