Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking up templates in Vue3 #96

Open
arpowers opened this issue Nov 11, 2019 · 41 comments
Open

Breaking up templates in Vue3 #96

arpowers opened this issue Nov 11, 2019 · 41 comments

Comments

@arpowers
Copy link

arpowers commented Nov 11, 2019

This is a follow up to a discussion in @yyx990803's workshop yesterday.

Specifically I wanted to register a vote for an ability to break up and organize template "chunks" along with reorganized functions/options in Vue 3's composition API.

Use case.. here is the code for the landing page you see at fiction.com

As you can see, the code is nearly 1000 lines long. The composition API will definitely help with this. However, the template alone clocks in at 150 lines. In this there are discrete areas of functionality like figures vs grids, etc..

The initial suggestion was to break things out into multiple components, however, this comes at a cognitive cost in jumping around to different files. As well as what you might call "file bloat"...

Im not sure of suggestions for the direct implementation details, however please consider its feasibility if you haven't already.

@ycmjason
Copy link

What ideas you have in mind? Is the goal of this to split template into multiple parts within the same file? If so, I think it is an interesting idea!

Would something like "named template" work?

<template>
  <use-template name="header" />
  <use-template name="main" />
  <use-template name="footer" />
</template>

<template name="header">
  <header>
    ...
  </header>
</template>


<template name="main">
  <header>
    ...
  </header>
</template>


<template name="footer">
  <header>
    ...
  </header>
</template>

Just throwing ideas. This is sort of replicating how you can assign virtual dom to variables in a render function; which could be quite useful if we figure a nice way to do it.

@JosephSilber
Copy link

I always wished for something like this 👍

The official answer for this has usually been to use additional components, but sometimes you want something lighter directly within the same file.

@chriscalo
Copy link
Contributor

chriscalo commented Nov 11, 2019

Building off of @ycmjason's proposal, what if each named <template name="Foo"> behaved just like a named component (<Foo/>) that you could just use in your main <template>? They could even be marked as functional components if you wanted to keep a clean separation.

This would be nice for situations where you'll never reuse these one-off components and you don't want the overhead of putting them in separate .vue files.

<template>
  <Header :user="user" />
  <Main />
  <Footer />
</template>

<template name="Header" functional>
  <header>
    <h1>Hello, {{ props.user }}!</h1>
  </header>
</template>

<template name="Main">
  <main>
    ...
  </main>
</template>

<template name="Footer">
  <footer>
    ...
  </footer>
</template>

@arpowers
Copy link
Author

So a functional template would be reusable in other files?

The syntax idea sounds great to me.

@ycmjason
Copy link

ycmjason commented Nov 12, 2019

I really like the idea of using functional component. However, Vue 3 is going to remove support for <template functional> (see here), so it doesn't look like to me that <template functional> adhere to Vue 3's philosophy much.

I think these named templates should be treated as "partials" that form the bigger part of <template>. An obvious way to do this is to make them inherit variables binding from wherever they are used.
But this approach reminds me of the old php days which makes me feel 🤢.

The following code example will make you 🤢

<template>
  <div v-for="i in 5"><use-template name="water" /></div>
</template>

<template name="water">
  <div>{{ i }}</div>
</template>

I am definitely more inclined to having named templates to have their own scoped and things passed in as "props". But we just need to find the right syntax to do this?

@Akryum
Copy link
Member

Akryum commented Nov 12, 2019

You can have implicit (not declared) props in Vue 3, so you could totally pass props to template-only components.

@ycmjason
Copy link

@Akryum but would this mean that, if vue team decided to go down this route, that each named template would create a new instance of vue component?

@ycmjason
Copy link

But I think Vue 3 component will be very cheap to create if they are template only? Combining with @Akryum's suggestion on using implicit props, we might be able to do:

<template>
  <Header :user="user" />
  <Main />
  <Footer />
</template>

<template name="Header">
  <header>
    <h1>Hello, {{ $props.user }}!</h1>
  </header>
</template>

<template name="Main">
  <main>
    ...
  </main>
</template>

<template name="Footer">
  <footer>
    ...
  </footer>
</template>

@Akryum
Copy link
Member

Akryum commented Nov 12, 2019

See #25

@chriscalo
Copy link
Contributor

+1 to having to pass props to these private components. Probably best if they aren't magically passed props from the parent scope.

They could explicitly access parent to get at its data, props, methods, etc.

@arpowers
Copy link
Author

I also wanted to add that since the whole point of this feature is better grouping stuff together in these huge components:

Not sure if it can get to something like this:

<template name="header">
  <header>
    ...
  </header>
</template>
<script>
function myHeaderStuffForCompositionAPI () {
  // ... 
}
</script>

@michaeldrotar
Copy link

What's the advantage of this over having separate components that are just a <template> section?

I don't understand the cognitive costs referenced in the description as I generally find smaller, focused files easier to mentally consume and understand... and this approach seems more like file bloat than separate components would since you're ending up with one large file.

When you're in your main template and you see <SomeTag ...> there's no indication if that's a component (under some-tag.vue) or a template name="some-tag" (or name="SomeTag") in the current file and if you have to scroll to somewhere else in your file to find it and understand it and then go back to where you left off in the same file to continue reading through the entire template -- this seems more painful than file switching and returning to exactly where you left off.

Explicitly passing props also makes this sound like separate components... or rather, it sounds like you want a way to define multiple, separate components in a single file?

@ycmjason
Copy link

@michaeldrotar

Sometimes the smaller parts of a large component does not make sense on its own. So it would be nice if those smaller parts could be local to the larger component. Regarding cognitive costs, it is down to personal preferences and ways of working. I don't think we are claiming superiority of named template over components in its own file.

I agree that <SomeTag ...> can be confusing of where it came from. It would be nice if we can distinguish between registered components and local components. Perhaps with some prefix? <$SomeTag ...>? (Again, just throwing ideas.)

@michaeldrotar
Copy link

Understood this approach would be optional, I'm just struggling to understand the use case -- to understand what problem it's solving.

The idea of simply separating one's layout pieces is not a very comprehensive use case... and if this is something where that's the only use case -- something most projects basically do once and rarely revisit -- then this is creating alot of work for something that's very narrowly focused.

Maybe better use cases would better illustrate the concern?

I can think of two times where I've personally considered something somewhat related to this idea:

Complex Components
Consider something like a material card -- it may be useful to have simpler patterns of two-way communication between the parent mdc-card component and its child mdc-card/actions, mdc-card/action-button, etc children. I think implicit prop access might be a strong point here, and it'd provide a reason for why mdc-card/whichever-child can't be used on its own: because it has to share state with its parent.

Complex Loops
I've ran into scenarios where I'm iterating some items in the template and the data model doesn't provide exactly what I need so I need some computed stuff... these are definitely cases where creating a separate component feels like overkill if I just need one computed and if it's something that's not reused anywhere else (and then I'm splitting my styles across files when they really aren't designed to be used independently). Alternatively I could create a computed that transforms the entire data set to one that's "view-ready", but this can also be alot of work with large sets and then I have trouble naming things... things and viewReadyThings..? Like.. urgh.

But that said...
Both of these scenarios are more of a sub-component then merely a sub-template use case so I don't know if these are way out of scope for what the author originally intended... and while it might be quick and dirty in the moment, having multiple template and script blocks seems like a decision I'd end up regretting at some point.

@IlCallo
Copy link

IlCallo commented Nov 13, 2019

Angular ng-template and ngTemplateOutlet system is one of the few things I miss since I moved to Vue.
The Angular syntax for those is pretty verbose (as always), but I guess there are ways to make it simpler keeping the modularity and code tidyness they provide.
Vue large components would benefit for that kind of API, some proposals in this issue are somewhat pretty similar.
Of course, clear and complete documentation of how that feature work doesn't exists in Angular official documentation 💚
But here some links anyway to take inspiration.
Official docs
A random guide

@michaeldrotar
Copy link

@IlCallo is this sort of a way to separate functionality and presentation? I'm not familiar with Angular but that's the gist I'm getting from the guide's <toggle> example.. that the toggle provides the methods and events so I'm assuming the layoutTemplate provides the presentation.

If so, would the composition API be another way to approach that? I see myself building pieces of functionality that components would use in whatever presentation they want. Then I could have a vertical-form-checkbox and a side-checkbox (or whatever) that both use the same checkbox functionality where the only differing code is whatever is necessary to make the different presentations for each component work properly... template, styles, minimal wiring.

@IlCallo
Copy link

IlCallo commented Nov 13, 2019

Not really, presentation and functionality are often mixed in web components.
Templates are used pretty much everywhere in Angular (if you dig enough deep), but in this kind of usage it's just too keep your code cleaner and allow structure reuse without having to create a new component, for those cases where it would be an overkill.

I can paste an usage example if you want some real world code where it came in handy.

@chriscalo
Copy link
Contributor

Relevant tweet:
https://twitter.com/adamwathan/status/1194250642791043073?s=21

Four years in, I think my only real @vuejs feature request is some sort of "multiple components in the same file" solution. I'd create many more tiny "private" components if this was easy.

Has there ever been any interesting discussion around this with syntax suggestions? 🤔

@CyberAP
Copy link
Contributor

CyberAP commented Nov 13, 2019

But your template code is actually 136 lines long. That's not a lot for such a component. The largest part of your SFC is CSS, which should be imported from a separate file in this case. The same thing applies to script declaration, where it's mostly about data structure, which should also be normalized.

As for the template code the suggestion of braking it up into multiple components does solve your issue. Not sure what you mean by cognitive cost when working with component composition. It's a standard paradigm in component frameworks that it's indeed created to decrease cognitive load and split your work into manageable parts. Merging multiple components into a single one defeats that purpose.

Moreover, introducing reusable templates will actually increase mental overhead for a developer. Imagine you have a scoped slot and within that slot you want to reuse a scoped slot variable. When you're working with that external template there's no easy way to tell where that variable came from, because it's not declared in component options, but rather is hidden somewhere in the markup.

<!-- container -->
<div v-slot="{ foo }">
  <template src="./bar.template.html" />
</div>
<!-- bar.template.html -->
<div>
  {{ foo }}
</div>

Also imagine a situation where your reusable template declares a slot. How you'd be able to tell what slots does your component have without peeking at every single reusable template?

<template>
  <template name="foo" />
</template>

<template name="foo">
  <slot />
</template>

To recap:

  • We already have component composition
  • Reusable templates are the same thing as a component, you'll have to (and you will) navigate to them anyway, even if they're in the same file
  • Those templates introduce mental overhead

I strongly vote against this feature since we already have a mechanism to reuse templates, which is a component composition. This feature would introduce more problems than solve.

@arpowers
Copy link
Author

arpowers commented Nov 13, 2019

@CyberAP

Isn't the whole angle with the composition API about "code organization"?

With that perspective you could say the same thing: "why create the composition API when you could just break things into separate components?"

Also, nothing introduces more friction for me in my projects than hopping between files and folders. Maybe I'm the only one...

However! I do agree with you that if there are substantial technical tradeoffs and "footguns" with this, then it might not be worth it. I just felt it warranted a discussion.

@chriscalo
Copy link
Contributor

(You're not the only one, @arpowers.)

When we're talking about plain functions, this is a widely-accepted best practice: when one function is starting to do too much or becomes hard to follow, it might be time to break some of that functionality into another function and call it from the first. And in doing so, there's no best practice that says we need to automatically move that function to another file. If it's only going to be referenced from this file, it's probably best to leave it here until the moment comes where it's needed elsewhere.

To others who don't have this pain or see the value in this proposal: it doesn't have to be useful to you to have value for others. As stated above, I have been in many situations where I wanted to break out a chunk of template for readability's sake even though I have no intention of using it anywhere other than in this component.

@michaeldrotar
Copy link

The composition API solves a need that doesn't have a great solution currently.. there are half measures like mixins and component composition, but they have major drawbacks. I wouldn't argue that this is the same thing.

I also don't think it can be an argument of someone not having to use this if they don't want to -- everything takes time to build so time spent on one feature is time away from another... plus the cost of maintenance after it's built. The core of Vue should be hyper-focused to features that everyone needs. Niche features can be implemented in 3rd party libs.

To address the use case in the original post (cause I'm just noticing it now).. I kind of have to disagree that this should all be in one file, regardless of whether or not it could have multiple <template> blocks... the template is only 136 lines, compared to 132 lines of script and 640 lines of css. I think the template is the least of the worries.

I'd break up the data into separate files: benefits, features, and compares. Data should always be separate from code, and it doesn't need to be in a database -- json files work great for this.. or even a js file that returns an object. Then you can use it from anywhere and non-tech team members can add new items or fix spelling mistakes without wading through a sea of code.

The css is definitely way heavier than the markup.. sometimes it's worthwhile to break something into a component simply because of the css.. especially once you're doing animations and transitions and making things responsive. You already basically have it split into components: .header, .benefits, .features, .compare, .alpha-program.

The fiction site is beautiful and you clearly know how to organize and structure code.. I have been trying to understand the motivation behind this idea but it feels like the issue is perhaps with your code editor making it hard to switch between files moreso than anything else..? 🤔 Saying that creating files or hopping between files is painful or time-consuming is a tooling issue, not a framework issue.

To the other people that agree with this proposal, please list some actual use cases. Even I tried to list some with my experiences with complex components and for loops, but my use cases are better suited to private sub-components than to breaking up templates.

@arpowers
Copy link
Author

The problem working across files happens when you go from say 5 components to 25 components as you try and break things out discretely as possible.

It gets harder to remember where things are and what things do without following the dependency chain each time.

Feel free to close this issue if it's too controversial to deal with right now. Let's note that it would be "purely additive" hence shouldnt be an issue to add in 3.X release.

@michaeldrotar
Copy link

I think that's a great point, and I think most people struggle with that at some point... I've definitely been at both extremes during my career -- either ending up with massive components that are everything in a page or tiny components that are barely more than a few lines of markup.

My goal is no longer to make components as discrete as possible, or even as reusable as possible.

There are 2 criteria I usually use:

repetition
If I've done the same thing more than a few times, it might be ready to make it a component -- if I do it too soon, I inevitably make some poor assumptions and structure it poorly, so I want to have the same pattern on a few different screens in different scenarios.

complexity/size
Once a component becomes so large that I can't digest what it does with a cursory scroll through the file, then it's time to break up the pieces.

If we look at something like the composition api, part of it's goal is to get the props, computeds, methods, etc that are all related to the same feature into the same block of code together so that the code looks like XXX YY ZZZ instead of XYZ XZ XYZ. I think it's the same deal for components -- once a component has 3+ logical "sections" than it's easy to split those into logical components just so it's easier to read one piece at a time.

@Akumzy
Copy link

Akumzy commented Nov 20, 2019

I don't know fragment is a reserved name already but having something like this would be great

    <template>
      <div>
        <FolderTree :data="{files}" />
      </div>
    </template>

    <fragment name="FolderTree" :data="{files}">
      <ul class="pl-4">
        <li class v-for="(file, index) of files" :key="index">
          <div>
            <input type="checkbox" name :id="file._id" />
            <span class="ml-2">{{file.name}}</span>
          </div>
          <div v-if="file.isOpen">
            <FolderTree />
          </div>
        </li>
      </ul>
    </fragment>

@anthonypenna
Copy link

I genuinely think that this would just increase confusion, without adding anything beneficial to the framework.

@aztalbot
Copy link

aztalbot commented Nov 21, 2019

I'm all for @ycmjason's idea (#96 (comment)). That seems easy to support, easy to optimize, and very similar to how you would approach the issue if you were writing JSX.


I was curious if something similar could be hacked together currently. I ended up with something like this:

<template>
  <div class="hello">
    <Define name="Link" v-slot="{ url, name }">
      <li>
        <a :href="url">
          <strong>{{ prefix }}:</strong>
          {{ name }}
        </a>
      </li>
    </Define>
    <Define name="Header" v-slot="{ title }">
      <h3>{{ title }}</h3>
    </Define>
    <Define name="LinkGroup" v-slot="{ title, links }">
      <Header title="Big Tech Companies"/>
      <ul>
        <Link v-for="{ url, name } in links" :key="name" v-bind="{ url, name }"/>
      </ul>
    </Define>

    <h1>{{ msg }}</h1>
    <Header title="Big Tech Companies"/>
    <ul>
      <Link url="http://google.com" name="Google"/>
      <Link url="http://apple.com" name="Apple"/>
    </ul>

    <LinkGroup title="Frameworks" :links="links"/>
  </div>
</template>

<script>
import Define from "./Define";

export default {
  name: "HelloWorld",
  components: { Define },
  props: {
    msg: String
  },
  data() {
    return {
      prefix: "Go to",
      links: [
        { name: "Vue", url: "https://vuejs.org" },
        { name: "React", url: "https://reactjs.org/" },
        { name: "Angular", url: "https://angular.io/" }
      ]
    };
  }
};
</script>

see Define.js in the sandbox for the functional renderless component that enables this: https://codesandbox.io/s/vue-template-47veo

It relies on the parent mounting after the children so the children can register functional components on the parent before render. So yeah, it's a hack. No idea on how easily it would break, but clearly no ones should do this. This is just for kicks.

But in terms of the ergonomics of this type of pattern, I guess i could see it being useful for groups of tightly coupled components that have repetitive patterns and specific characteristics (not very generic or useful elsewhere). I can't say I've ever really run into cases where I would find this useful, though. Ordinarily companion components grow and benefit from separate files in my experience (promoting private components to separate files would be super easy in @ycmjason's example, which I like).

This type of pattern could probably be natively supported (i.e. compiler could optimize for it, like hoisting the defined component if not referencing anything from parent scope). The only advantage of this approach, though, is access to the parent scope. Otherwise, it's more clunky and and less accessible to newer devs compared to the simplicity of @ycmjason's example.


Update: I was also able to get it to work like this, which might look a little cleaner

<template>
  <div class="hello">
    <Components>
      <template #Link="{ url, name }">
        <li>
          <a :href="url">
            <strong>{{ prefix }}:</strong>
            {{ name }}
          </a>
        </li>
      </template>
      <template #Header="{ title }">
        <h3>{{ title }}</h3>
      </template>
      <template #LinkGroup="{ title, links }">
        <Header title="Big Tech Companies"/>
        <ul>
          <Link v-for="{ url, name } in links" :key="name" v-bind="{ url, name }"/>
        </ul>
      </template>
    </Components>

    <h1>{{ msg }}</h1>
    <Header title="Big Tech Companies"/>
    <ul>
      <Link url="http://google.com" name="Google"/>
      <Link url="http://apple.com" name="Apple"/>
    </ul>

    <LinkGroup title="Frameworks" :links="links"/>
  </div>
</template>

However, I'm not sure this hack would work at all in the Vue 3 template compiler. Based on the output render function from the compiler, it looks like it tries to resolve the components all at once, before the renderless component would have a chance to register the nested components. So, Vue 3 would have to have a special built-in component like <components> from where it would take the slots and create components that are then available to rest of the component render function (or this functionality could be added to the existing <component> built-in perhaps). If it were supported in Vue 3 you could envision this:

<template>
  <components>
    <template #Link="{ url, name }">
      <li>
        <a :href="url">
          <strong>{{ prefix }}:</strong>
          {{ name }}
        </a>
      </li>
    </template>
    <template #Header="{ title }">
      <h3>{{ title }}</h3>
    </template>
    <template #LinkGroup="{ title, links }">
      <Header title="Big Tech Companies"/>
      <ul>
        <Link v-for="{ url, name } in links" :key="name" v-bind="{ url, name }"/>
      </ul>
    </template>
  </components>

  <h1>{{ msg }}</h1>
  <Header title="Big Tech Companies"/>
  <ul>
    <Link url="http://google.com" name="Google"/>
    <Link url="http://apple.com" name="Apple"/>
  </ul>

  <LinkGroup title="Frameworks" :links="links"/>
</template>

<script>
import { reactive, ref } from "vue";

// rather than creating a new component with state, just compose more state here
function usePrefix() {
    return { prefix: ref("Go to") }
}

function useLinks() {
  const links = reactive([
    { name: "Vue", url: "https://vuejs.org" },
    { name: "React", url: "https://reactjs.org/" },
    { name: "Angular", url: "https://angular.io/" }
  ]);

  return { links };
}

export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  setup() {
    return {
      ...usePrefix(),
      ...useLinks()
    };
  }
};
</script>

I want to say that I would like something like this. There is no need for more templates in different parts of the file. You aren't shoving multiple components in the file. It's still a single file component. But, it allows you to easily create reusable templates, and if they require new pieces of state or logic, it's really easy to drop that in using composition. No need to create a new component with a single data attribute or computed. If needed, it's easy to copy the template and associate composition functions to a new file. And if unrestricted access to parent scope is a concern, you can maybe add a scoped prop to the template so that only a subset of the parent data is passed to the scoped slot (eg data.Link by default for the Link slot).

However, this is a change to how the template compiler works. So even though slots are already idiomatic to the framework, this would not be an insignificant change. And it's not clear to me if there would be any side effects from adding this ability. The other option of just including fragment template that use $props seems much simpler and more of a vue loader change, rather than affecting the framework itself. However, I find it a little limited. The above would feel much more useful and natural to me.

@SamWoolerton
Copy link

I've hit a similar issue a couple of times, and breaking out the repeated markup didn't really make sense as it was tightly coupled to the parent component and couldn't be used elsewhere.

I found JSX to be really useful - you can easily make small components (with logic too if you need) in the same file.

Not sure that the template syntax would add much given that JSX exists

@arpowers
Copy link
Author

@SamWoolerton interesting thought, would it be possible to get a link to somewhere this is done in JSX + Vue? It would def be a good study.

@samwoolertonLW
Copy link

@arpowers Here's a basic overview
From memory, the Vue CLI has support baked in so no config required either - just write component in the script section, register and use as normal

JSX is a bit different with event handlers etc but not that bad, especially if you're mainly doing this for repeated markup

@arpowers
Copy link
Author

@samwoolertonLW thanks! It's probably worthwhile to learn a few things about the advantages of JSX and why it exists anyway.

@aztalbot
Copy link

Not sure that the template syntax would add much given that JSX exists

Currently this is true. But, with Vue 3, the template syntax provides additional optimizations. It would still be good to have a way to do this sort of thing using the template syntax. That way, folks don't have to forgo those optimizations, introduce an additional syntax to the project, or split small components into separate files when it feels unnecessary.

@Leokuma
Copy link

Leokuma commented Jan 14, 2020

Currently this is true. But, with Vue 3, the template syntax provides additional optimizations. It would still be good to have a way to do this sort of thing using the template syntax. That way, folks don't have to forgo those optimizations, introduce an additional syntax to the project, or split small components into separate files when it feels unnecessary.

In addition, the Vue spec was based on the Web Components spec, which uses templates. I believe Vue is more oriented towards "extending" official standards rather than inventing its own.

@amcsi
Copy link

amcsi commented Jun 30, 2020

What what be even more amazing besides being able to have multiple reference-able template tags would be to also be able to reference those template tags in the render() function.

That way you could have a more dynamic component that need more power than simple template tags can provide, but while keeping the ability to use the elegant <template> syntax and not have to use hyperscript or JSX.

I mention this in a ticket in vue-loader in the last paragraph: vuejs/vue-loader#1686

@smolinari
Copy link
Contributor

Just ran into this myself.

The initial suggestion was to break things out into multiple components, however, this comes at a cognitive cost in jumping around to different files. As well as what you might call "file bloat"...

As far as I am concerned, that suggestion is the tool. If the html can be broken out with its relative logic, and can be reusable or has specific purpose (reason for change), then it should be its own component. It isn't file bloat, it's SoC and SRP.

Scott

@intrnl
Copy link

intrnl commented Sep 19, 2021

I honestly feel that it's necessary to bring this discussion up again, but the problem I have with single file components is that sometimes I want to reuse parts of a template in different places within a component, but creating a separate file for it seems to be quite the hassle.

Personally I feel that only being able to break up markup templates is too limiting, we should be able to define styling and logic for it. Below is an example for "fragments", and while this could be simplified to without using "fragments", I can at least say that the need for reuse does come up from time to time.

<template>
  <div v-for='...'>
    <message-divider />
  </div>
</template>

<fragment name='message-divider'>
  <template>
    <div class='divider'></div>
  </template>

  <style scoped>
    .divider {
      border-top: 1px solid red;
    }
  </style>
</fragment>

The other thing I feel is necessary, is that in addition to reusing "fragments" in multiple places within a single component, is that we should be able to colocate several small components into one file and have them exportable. Though of course if we're defining a template that's not within a "fragment" then it should be a default export, and we could have a private attribute that prevents "fragments" from being exported.

<fragment name='manga-list'>
  <script setup>
    // ...
  </script>

  <template>
    ...
  </template>

  <style scoped>
    /* ... */
  </style>
</fragment>

<fragment name='manga-entry'>
  <script setup>
    // ...
  </script>

  <template>
    ...
  </template>

  <style scoped>
    /* ... */
  </style>
</fragment>
import { MangaList, MangaEntry } from './Manga.vue';

// ...

@intrnl
Copy link

intrnl commented Sep 19, 2021

Another compelling reason for breaking it up is for the new Suspense feature. Unlike Svelte where there is a logic block that handles promises directly, we rely on Suspense which is different as it delegates showing placeholders to its parent component (this delegation is super nifty for composition)

So there could be a pattern where you'd define both the placeholder and the actual component in the same file, while also adding a default component that wraps them both for you (if needed be)

<script setup>
  // ...
</script>

<template>
  <suspense>
    <template #default>
      <manga />
    </template>
    <template #fallback>
      <manga-fallback />
    </template>
  </suspense>
</template>

<fragment name='manga'>
  ...
</fragment>

<fragment name='manga-fallback'>
  ...
</fragment>

@ThorFjelldalen
Copy link

After coming from Flutter, I would echo this request, as you often split up larger/more content heavy components into smaller methods, so that they don't clutter the base component. It offers more readable code, as you can explain with your own names what parts make up this component. A long page with a lot of static markup doesn't always make sense to move to its own file, like when splitting up a complex function into private helper methods.

I'm a big fan of SRP, and I think this feature would only encourage more clean code, and make it easier to split into separate components when the need for reuse arise.

@WilliamStam
Copy link

another voice here.

use case

<tr>
                    <th class="text-end"> Surname:</th>
                    <td>
                        <template v-if="update?.surname && consumer.surname != update.surname">
                            <span class="text-danger">{{ consumer.surname }}</span> ->
                            <span class="text-success">{{ update.surname }}</span>
                        </template>
                        <template v-else>
                            {{ consumer.surname }}
                        </template>
                       </td>
                    <td></td>
                </tr>
                <tr>
                    <th class="text-end"> Printable Name:</th>
                    <td>
                        <template v-if="update?.print_name && consumer.print_name != update?.print_name">
                            <span class="text-danger">{{ consumer.print_name }}</span> ->
                            <span class="text-success">{{ update.print_name }}</span>
                        </template>
                        <template v-else>
                            {{ consumer.print_name }}
                        </template>
                    </td>
                    <td class="text-end"></td>
                </tr>

would be really nice to be able to template those rows. right now i have lots of copy paste. using SFC on this (vue 3.2)

the cognitive costs argument should be voided tho right? isnt that up to the developer to do? the whole concept of using something like vue and components is to not repeat yourself.

@hacknug
Copy link

hacknug commented Nov 30, 2022

would be really nice to be able to template those rows. right now i have lots of copy paste. using SFC on this (vue 3.2)

In this case I would use v-for to loop an array with the two things you need there, the label for the table heading and the key to check update and consumer.

@WilliamStam
Copy link

WilliamStam commented Nov 30, 2022

so i guess if the "data parts" are from different sources then you would have to create a new array with them in to be able to use it.. so what if you want to have some other content between it then you sorta sol. not sure how it would work tho. right now just gave up and creating a file for each part but its REALLY messy (3 lines of template, 4 lines of <template></template><script setup lang="ts"></script> and 1 line of const props = defineProps). im not going to reuse these parts but what if im going with recursion?

wouldnt it just be easier to just be able to create a component in another? vue prides itself in putting the power to the developer. so from "cogative" sense this is a moot point. but if there are technical reasons.. then so be it i guess.


edit: just realized my comment has "tone" in it. sorry. not ment to have. just had a frustrating day. your suggestion is somewhat valid. i could built up a content tree in an object / array. but then i might as well just copy and paste the lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests