Vue: Proposal: Multiple components in .vue files

Created on 9 Apr 2019  路  7Comments  路  Source: vuejs/vue

What problem does this feature solve?

Other frontend frameworks, notably React, make it quick and easy to create many small components in a single file. This pattern promotes code reuse, component composition, and DRYness.

While Vue has this ability, it feels hacky and sacrifices much of the clarity and usability that .vue files provide.

This aim of this proposal is to define a clear and intuitive API for defining multiple components in a single .vue file.

What does the proposed API look like?

<component>

The <component> element is the building block of multi-component .vue files. Used as a top-level tag in .vue files, it's effectively a self-contained "single-file component", and it can contain anything that is valid in single-file components as they're currently defined. A .vue file can contain any number of <component> elements. They are evaluated from top to bottom鈥攏o hoisting. The element accepts two properties that control its availability to other parts of the code:

  • name (string, no default)

    The name of the variable that the component is assigned to. Components defined lower in the file can reference this component by its name. Additionally, if the component is exported (see below), this is the name under which it will be exported.

  • export (boolean, default false)

    Whether or not the component is exported. If a component is exported, but doesn't have a name, it will be the default export. More than one exported component without a name would create multiple default exports, and is therefore an error. All components must have either name or export or both.

By using the <component> element, individual .vue files gain a great deal more flexibility and power. They can compose a single exported component from many small child components, they can export many small components used throughout an application, they can export a main default component and less frequently used named components, and so on.

Examples

An isolated component (Foo) only used by another component in the same file:
<component name="Foo">...</component>
<component export>
    <script>export default { components: { Foo } };</script>
    <template><foo /></template>
</component>
Named and default exports:
<component export name="Foo">...</component>
<component export name="Bar">...</component>
<component export>...</component>

These would be imported in another file with import Baz, { Foo, Bar } from './Baz.vue';

An illegal file due to multiple default exports
<component export>...</component>
<component export>...</component>
An illegal file, because it combines the multi-component format and the single-component format
<component name="Foo">...</component>
<script>export default { components: { Foo } };</script>
<template><foo /></template>

Backwards compatibility

The addition of the <component> element would not be a breaking change鈥攕ingle-file components with no top-level <component> tags would continue to function exactly as they do today. As mentioned in the example above, combining the single-component format with the multi-component format is an error.

A potential point of conflict is the existing <component> tag than can be used in templates. I don't foresee this being a problem, since the usage of <component> as a top-level tag in .vue files is well defined, but I can't say for sure.

discussion feature request

Most helpful comment

Right now, this should be achievable in userland with a loader that transforms the components into inline components

I have never felt the need to create small components in the same file. Having different files makes it so much easier to find the components inside of a project.

The only reason I would have a component created inside of an existing component in a vue file if it's used only there. And in that scenario, I would rather use JSX to achieve it.

To me enabling this is a big risk to make it easier to drastically impact project maintainability by more junior developers. Especially if we allow exporting multiple components in the same file.

All 7 comments

Right now, this should be achievable in userland with a loader that transforms the components into inline components

I have never felt the need to create small components in the same file. Having different files makes it so much easier to find the components inside of a project.

The only reason I would have a component created inside of an existing component in a vue file if it's used only there. And in that scenario, I would rather use JSX to achieve it.

To me enabling this is a big risk to make it easier to drastically impact project maintainability by more junior developers. Especially if we allow exporting multiple components in the same file.

@posva Thanks so much for your feedback 馃槃

Right now, this should be achievable in userland with a loader that transforms the components into inline components

Neat! That didn't occur to me. I'll look into it.

The only reason I would have a component created inside of an existing component in a vue file if it's used only there. And in that scenario, I would rather use JSX to achieve it.

That is definitely the most common use case in libraries that allow multiple components per file, and I would foresee it being the same in Vue. However, there are other use cases; for example, you could have a single file that exports a bunch of small, common, presentational components, like SVG icons. Having dozens of tiny files in this scenario is, I would argue, less convenient than putting them all in a single file, and it makes for more verbose imports:

import Foo from './icons/Foo';
import Bar from './icons/Bar';

// vs.

import { Foo, Bar } from './icons';

This could be avoided by using an index file, but then you have to maintain it and remember to add additional icons to it, or add even more tooling to do it for you.

I disagree about using JSX, however鈥攐ne of the reasons I like Vue is because I can get away from JSX, and using JSX alongside Vue's template syntax seems needlessly inconsistent and hacky to me.

This could be avoided by using an index file, but then you have to maintain it and remember to add additional icons to it, or add even more tooling to do it for you.

That tooling would be much less than what is necessary to support this though

I disagree about using JSX, however鈥攐ne of the reasons I like Vue is because I can get away from JSX, and using JSX alongside Vue's template syntax seems needlessly inconsistent and hacky to me.

I understand getting away from JSX. The rest isn't true though because it would be JSX only so not inconsistent. It isn't hacky

Upon reading the title, I was initially against this proposal. However, after reading the description I think I can get behind this. The only thing I would change is that only ONE component can be exported. Other than that, I think this is a great idea to organize tightly coupled components or single-use components.

<template>
  <div>
    <comp-a />
  </div>
</template>

<script>
export default { /* ... */ }
</script>

<component name="comp-a">
  <template></template>
  <script></script>
</component>

Basically, for every <component> tag, add it to the components map of the main export. It also avoids JSX which is a bonus in my book.

Edit: Another benefit is that factoring out the nested component is a simple copy-paste since it follows the exact structure of a standard .vue file.

That tooling would be much less than what is necessary to support this though

Agreed! But you could make the same argument about any tool that has value: writing a few small helper functions is much less work than supporting lodash, after all.

Flip things around鈥攊f this feature already existed, the idea of removing it in favor of maintaining index files would be absurd.

I understand getting away from JSX. The rest isn't true though because it would be JSX only so not inconsistent. It isn't hacky

You'd have some components written in Vue's template syntax, and some components written in JSX. That strikes me as inconsistent and hacky.

I don't think we will support this in core. The assumption that one SFC file corresponds to one component is somewhat important for tooling implementations (hot module replacement, IDE support, etc.) and I honestly don't see the complexity being worth it.

I don't think we will support this in core. The assumption that one SFC file corresponds to one component is somewhat important for tooling implementations (hot module replacement, IDE support, etc.) and I honestly don't see the complexity being worth it.

IDE support on default exports is bad, as it's too hard to understand them. That's why named exports are better, for IDE support, consistency in the project, etc....

I don't want to open a new ticket, but can named exports be considered for SFC files? So that IDE auto-import feature can actually works?

Right now, as a workaround, we create another .js file where we do a re-export, and though it's easy to make that, it should not be needed imho.

Was this page helpful?
0 / 5 - 0 ratings