Skip to content

Component setup using ES6 class works out of the box in Vue 3, why not officially support it! #3373

@sreenaths

Description

@sreenaths

What problem does this feature solve?

I just discovered that we could use ES6 class for creating components instead of the Composition API setup function. The best thing is it works out of the box in Vue 3 without any dependency on vue-class-component or other libraries! Even the experimental template interpolation service in Vetur is working flawlessly with it!!!

Surprisingly I couldn't find this way of creating components in any of Vue 3 documentation. Not sure why, or am I missing something!?
With official support, we could make the experience even better.

Why use ES6 class:

In the current way of creating a component using setup function & composition API, users are encouraged to create closure functions. When the number of instances of a component is minimal this doesn't make much difference. But when there are so many instances of the component, multiples of those many functions objects would be created, which doesn't look ideal.

For instance, 1000 instances of the following component would create 1000 getUserRepositories function objects. Which is overkill.

import { fetchUserRepositories } from '@/api/repositories'
setup (props) {
  let repositories = []
  const getUserRepositories = async () => {
    repositories = await fetchUserRepositories(props.user)
  }
  return {
    repositories,
    getUserRepositories
  }
}

What does the proposed API look like?

This is how we could create a Counter component using ES6 class & composition API. And it works with the current release of Vue 3!

<template>
  <div class="counter">
    Counter: {{ valueInternal }}<br />
    <button type="button" @click="onAdd">Add</button>&nbsp;
    <button type="button" @click="onSubtract">Sub</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, Ref } from "vue";

class Counter {
  valueInternal: Ref<number>;

  constructor(val: number) {
    this.valueInternal = ref(val);

    // Guess following binding & convertion to own properties are needed because of hasOwn check @ https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/componentPublicInstance.ts#L293
    // We must be able to remove it with official support
    this.onAdd = this.onAdd.bind(this);
    this.onSubtract = this.onSubtract.bind(this);
  }

  onAdd() {
    this.valueInternal.value++;
  }

  onSubtract() {
    this.valueInternal.value--;
  }

  someOtherMethod() {
    // Statements
  }
}

export default defineComponent({
  props: {
    value: Number
  },
  setup: props => new Counter(props.value || 0)
});
</script>

I have committed a dummy project with working example @ https://github.com/sreenaths/vue3-composition-class

What could we do through official support:

There could be more, but the following are some of the items that came to my mind.

  1. Direct default export of the class from inside the script tag. export default Counter instead of this working snippet export default defineComponent(props => new Counter(props));.
  2. Some way to bypass hasOwn check explained above in Counter constructor so that inherited properties can be accessed from inside the template - Can we use decorators for that?
  3. Should we have an interface for type-checking?
  4. When directly exporting the Class, can we provide some way to pass props definition?
  5. Documentation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions