sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.8k stars 4.24k forks source link

Allow to specify custom base class for Svelte Components #4168

Open kmmbvnr opened 4 years ago

kmmbvnr commented 4 years ago

Could we have an option to specify a custom base class for Svelte components?

<script>
import {BaseSvelteComponent} from './base';
</script>

<svelte:options base="BaseSvelteComponent"/>

base.js:

import {SvelteComponent} from 'svelte';

export class BaseSvelteComponent extends SvelteComponent {
 /* or could be completly own custom impl */
}

It would step to make Svelte a more generic compiler and could move many conflict decisions out of the scope of Svelte project.

It allows end-users of the compiler to make own decision. I think it could resolve things like Component inheritance, move towards a solution for Custom element without shadow DOM and Support attachShadow({mode: 'closed'}), handle Native HTML elements and Form-associated custom elements and minor things like Define some attributes on Custom Elements

And it seems pretty easy to implement - https://github.com/kmmbvnr/svelte/commit/4a7f3171050dea3d676bf928bbfb6644d09e0359

TehShrike commented 4 years ago

Why? Inheriting from HTML elements doesn't get us any closer to using vanilla Svelte components inside the shadow dom. Is there any other motivation?

In general, inheritance is bad. We don't want to give people any encouragement to use it. Prefer composition.

kmmbvnr commented 4 years ago

I think commandment about composition vs inheritance more applied to business logic stuff, till there we have mostly technical question.

Svelte may compiles to Web Component, by producing custom subclass of web browser HTMLElement. New Web Component specs includes new form-associated base classes It is the only way to achieve native browser behavior with web component compiled svelte code.

Beyond that, it solves other issues listed by me in the ticket description

Probably, some more compositional approach, would be split svelte and svelte compiler, to allow just compile shadow dom related js code, without other framework-related things

WillsterJohnson commented 3 years ago

+1 to component inheritance (in any form)

I'm currently building a component library and having to redeclare the same few lines of code for every component is going to get old fast and be a huge pain to maintain. The best part is that the result for my components is a pretty basic (but still important) feature.

pournasserian commented 2 years ago

+1 to component inheritance (in any form)

willnationsdev commented 1 year ago

Please forgive my JS inexperience. I see how the specified feature (#8991) allows one to extend the SvelteComponent-generated class and customize it, but I don't see how it allows one to specify that it inherits from a particular class (whether that be HTMLElement or some custom BaseSvelteComponent). Am I missing something? Is there some way to use the constructor argument in <svelte:options>'s new extend function to modify the inheritance hierarchy or mix in the members of another class or something? Without an example to demonstrate, I don't see how it implements this feature request.

dummdidumm commented 1 year ago

Yes this was closed by accident, you're right that it only allows to extend, not set a new base class.

willnationsdev commented 1 year ago

On a related note, I've been experimenting with using mixins as a workaround, and one issue I've run into is that, when I have a lot of properties added to a mixin that I intend to sync back to the DOM, I have to inline all property definitions in every customElements.props section where it's used because it requires a statically analyzable object literal. You can't just create a const object elsewhere that's imported by a <script context="module"> tag.

For example, I have a ContentElementMixin(Base) that adds a variety of properties intended to be reflected back to the DOM, including displayType, contentType, and contentId. If I then want to create a <content-menu-item> element that uses the mixin, I can't store the props definitions for those base properties in a single location; they must be inlined in the Svelte component options every time I use the mixin somewhere:

<svelte:options customElement={{tag: "content-menu-item", extend: ContentElementMixin, props: {
    displayType: { reflect: true, type: "String", attribute: "display-type" },
    contentType: { reflect: true, type: "String", attribute: "content-type" },
    contentId: { reflect: true, type: "String", attribute: "content-id" },
}} />

<script content="module">
  import { ContentElementMixin } from "./mixins.js";
</script>

Given that I'm able to import ContentElementMixin, might it be possible to at least be able to import an object literal expression from elsewhere as well? Not sure how viable that is given the synchronous & isolated nature of the JS parser, but it's worth mentioning as a pain point anyway.