Subscribe on changes!

setup() in extended component is not run

avatar
p3k
Feb 18th 2021

Version

3.0.5

Reproduction link

https://jsfiddle.net/mL4s8k2b/3/

Steps to reproduce

Extending a component having a setup() method does not provide the return value of that very setup() method to the extending component.

This is reflected in the console log: it shows the messages “Hello from normal setup” and “Extends created” but it does not show the message “Extends setup”, i.e. the setup() method in the extended component is never run.

What is expected?

The display of the message “Extends setup”.

What is actually happening?

The message is not displayed.


I mostly copied this issue from #3017 but IMHO it was closed prematurely linking to a comment in issue #1866:

This is intentional. mixins is a part of the Options API and setup is part of the Composition API. If you are using the Composition API, all logic composition should be done using Composition API.

As this is incorrect (both, mixins and extends appear in the docs under composition) and the comment does not even reason about extends I kindly ask for further clarification.

avatar
Feb 18th 2021

Your confusion likely stems from ambiguous terms:

  • "Composition" as a general term refers to ways in which component logic can be extracted, re-used, and composed in multiple components.
  • "The Composition API" refers to all the features provided by and used in setup().

Composition can be achieved by using mixins/extends or by using functional composition in setup - the former was the only way of composition in Vue 2, the latter is a new, alternative way provided in Vue 3.

One of the major reasons for the creation and design of the composition API were issues and shortcomings of the mixin pattern (read more here), so it's a conscious design decision to not have setup bleed back into the mixins pattern.

When using setup, code reuse should be achieved by extracting logic into functions that can be composed inside a component's setup function - not mixins.

avatar
p3k
Feb 18th 2021

thanks for the reply, @LinusBorg.

code reuse should be achieved by extracting logic into functions that can be composed inside a component's setup function

would you have an example showing what you mean by this? like: here is an extend pattern, and here is the composition api implementing this pattern…?

at the moment i am doing it like this, achieving the “bleeding back” into the extends pattern:

import Page from "./page.vue";

export default {
    extends: Page,

    setup() {
        return { ...Page.setup() };
    }
};
avatar
Feb 18th 2021

Good example - good as in: where it gets a bit philosophical.

  • Mixins and composition functions both are used to share code like methods, data, lifecycle hooks etc, usually - not a whole component.
  • extend, technically, is also just a mixin but can be used like you do - write a base component and then extend from it, much like you would extend from a class in OO.

Now, composition functions are strictly about sharing behavior - you can not share component config, template/render function etc with it. So I cannot give you an equivalent of what you do with a composition function alone.

While possible to achieve this extending behavior with extends (when not using setup, or using the workaround you did), I never found a good use for it, so I would like to understand where you find that to be useful.

In terms of patterns, it's usually more flexible, architecture-wise, to not extend from other components, but compose them - i.e. wrap a base component in another one that can add behavior, or add one in a slot as a child to do that. Extending components in this OO fashion is, in my peception, kind of a relic that doesn't fit well into the way modern frontend code is composed. We don't even cover it in the official guide afaict, just document it in the API section for completeness' sake.

So my reply would be: If you like to do this, use your work around. it's fine, technically. We don't feel that this is a pattern that should be put to the fore-front.

I'm however open to discuss a more specific example and how I would approach it.

avatar
p3k
Feb 23rd 2021

thanks for the insights, i always like some philosophical arguments, too.

i am now trying to apply your suggestions and this is what i came up with:

import { components, props, setup } from "./page.js";
import MyComponent from "./my-component.vue";

export default {
    components: { ...components, MyComponent },
    props,
    setup
};

is this the right approach to composing components?

furthermore, i would be interested how to manage a growing amount of such composition functions. if i am not mistaken, those act like interfaces (not in the literal object oriented way) – they need a specific input and provide a specific output.

how would one not get lost in code, especially if there are plenty of those functions? (pointers are appreciated, too.)

avatar
Feb 23rd 2021

That seems like a way to achieve that, but It's always hard to give more specific advice on an abstract example like this one. For example, I'm not sure why you would import the page's child components, and not have a render function on this new created function. I lack context to understand you actually want to achieve.

furthermore, i would be interested how to manage a growing amount of such composition functions.

Just so we're clear: What you do in your example is not a composition function. you are composing component options. A "composition function" would be something like this:

https://youtu.be/Soi7Z6gOQE0?t=2466

If you want to discuss further, visit chat.vuejs.org to talk with the community (you can find me there as well). The Issue tracker is not the right place for discussions about patterns etc.

Also I will close this issue as there's no bug.

avatar
Jan 23rd 2022

在我看来extends和mixins就不应该在hooks里出现,与设计违背