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

[Vue v3] Changes for @Component decorator and Vue base class #406

Open
ktsn opened this issue Mar 19, 2020 · 61 comments
Open

[Vue v3] Changes for @Component decorator and Vue base class #406

ktsn opened this issue Mar 19, 2020 · 61 comments
Labels

Comments

@ktsn
Copy link
Member

ktsn commented Mar 19, 2020

Summary

  • @Component will be renamed to @Options.
  • @Options is optional if you don't declare any options with it.
  • Vue constructor is provided from vue-class-component package.
  • Component.registerHooks will move to Vue.registerHooks

Example:

<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
</template>

<script>
import { Vue, Options } from 'vue-class-component'

// Component definition
@Options({
  // Define component options
  watch: {
    count: value => {
      console.log(value)
    }
  }
})
export default class Counter extends Vue {
  // The behavior in class is the same as the current
  count = 0

  increment() {
    this.count++
  }
}
</script>
// Adding lifecycle hooks
import { Vue } from 'vue-class-component'

Vue.registerHooks([
  'beforeRouteEnter',
  'beforeRouteLeave',
  'beforeRouteUpdate'
])

Details

As Vue v3 no longer provides base Vue constructor, Vue Class Component will provide it instead. Fortunately, we can add class component specific features in the base class as we define it in Vue Class Component package.

One of the benefits with the new Vue constructor is we can make @Component decorator optional. There were non-trivial number of confusions regarding missing @Component decorator in super class / mixins (e.g. #180). Making decorator optional would solve this problem.

Also renaming @Component with @Options would make more sense as it is for adding component options to your class components rather than making a class component.

Since @Options is optional, having registerHooks on the decorator will not work. So we will move the method under static field of Vue constructor.

Alternative approach

Declaring component options as static properties

We could do this to define class component options:

export default class Counter extends Vue {
  // All component options are static properties
  static watch = {
    count: value => {
      console.log(value)
    }
  }

  count = 0

  increment() {
    this.count++
  }
}

But it has a drawback: we cannot define static properties / methods not for component options both on userland and library side. e.g. we cannot define static method like registerHooks on Vue.

@ktsn ktsn pinned this issue Mar 19, 2020
@haoqunjiang
Copy link
Member

Seems some of the breaking changes may not be fully covered with a codemod.
Is it possible to implement a runtime compatibility layer on top of it in vue-property-decorator?

@ktsn
Copy link
Member Author

ktsn commented Mar 19, 2020

Yes, we can provide a runtime compatibility layer. Just wrapping Options with Component as Options provides almost the same API interface and feature with the current Component.

Maybe providing Options and Vue from vue-class-component with minor update would help to migrate.

@nicolidin
Copy link

Is there a way to already test it with the vuejs/composition-api ??
Thanks!

@ktsn
Copy link
Member Author

ktsn commented Mar 25, 2020

I'm going to create a bridge api for composition functions in a separated issue. -> Edit: created #416

@ktsn ktsn added the v8 label Apr 12, 2020
@ktsn ktsn mentioned this issue Apr 26, 2020
@the-emerald
Copy link

Does the abandonment of the Class API proposal have any affect on this package and whether it would be maintained at the same level in the future? Cheers.

@HIMISOCOOL
Copy link

Does the abandonment of the Class API proposal have any affect on this package and whether it would be maintained at the same level in the future? Cheers.

nope, from what I understand this package will always exist and will always have 1st party package status, its just not practical for the base vue package to use classes in todays world.

@LifeIsStrange
Copy link

@ktsn So we did try the latest vue class component beta and while the new props declaration is working just fine, we need to have Watch, Provide and other related Vue features.
Putting watch in @options works but is not expressive enough at all. The static alternative is not yet implemented but would suffer the same issue being static ?

The issue is that we were able to access the class variables through this inside the method that react to the thing it watch.
This expressivity is needed for a lot of behavior and we are unable to migrate to Vue3 until this crucial limitation is fixed.

@calebeaires
Copy link

@LifeIsStrange like you, our team share with those concerns. Waiting for an update!

@iklemm
Copy link

iklemm commented Nov 1, 2020

Seems that this new API does not match with documentation https://class-component.vuejs.org which is very confusing.

@Chris2011
Copy link

Chris2011 commented Nov 8, 2020

@LifeIsStrange I think having Watch and Provide and other decorators for class methods and fields are part of vue-property-decorators, isn't it?

Already mentioned in the documentation: You may also want to check out the @Prop and @Watch decorators provided by Vue Property Decorator.

@LifeIsStrange
Copy link

@Chris2011
Vue property decorator has not be ported to vue 3 and nobody is working on it for the foreseeable future. This is very worrying for the industry.

@Chris2011
Copy link

@LifeIsStrange well, vue-property-decorator depends on vue-class-component, so I guess they will farely wait for the new version 8 of this package here. :). Just my opinion, no insides.

@calebeaires
Copy link

Hope this situation changes. This two libs belongs to the top of mind of the Vue community!

@LifeIsStrange
Copy link

@Chris2011 that's not how it should work, they should begin working on it while it's in beta. If they need to do some breaking changes to vue class component it will be too late after a stable release

@ZhengXingchi
Copy link

ZhengXingchi commented Dec 1, 2020

how can we modify a msg in watch method like that

image

<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
</template>

<script>
import { Vue, Options } from 'vue-class-component'

// Component definition
@Options({
  // Define component options
  watch: {
    count: value => {
      console.log(value)
      if(value==10){
        this.msg='计数超过10了'
      }
    }
  }
})
export default class Counter extends Vue {
  // The behavior in class is the same as the current
  count = 0
  msg=''

  increment() {
    this.count++
  }
}
</script>

@ZhengXingchi
Copy link

@ktsn

@ygj6
Copy link
Member

ygj6 commented Dec 8, 2020

@ZhengXingchi this cannot be used in arrow functions, the following code is work
watch: {
count( value) {
console.log(value)
if(value==10){
this.msg='计数超过10了'
}
}
}

@theoephraim
Copy link

Maybe it's time that vue-class-component and vue-property-decorator merge into a single tool? It seems like it would be easier to design a better and more wholistic solution this way.

@nicolidin
Copy link

I totally agree with @theoephraim, I think it would be better and more pragmatic for the class component ecosystem to merge these two libraries.
I also think it is the wish of many users of this library.

@calebeaires
Copy link

I am with you. I use class component style to my project. Don't migrate to Vue 3 until this movement is mature!

@aliibrahimroshan
Copy link

aliibrahimroshan commented Dec 18, 2020

I am migrating to vue 3 . I am not be able to access the props value in my class instead it gives me 0 and not printing in console the latest value i am using on prop. On template it working fine but i want the lateast value of titleComponent in my class

[

import { Options, Vue } from "vue-class-component";


@Options({
  props: {
    titleComponent: Number
  }
})

export default class StorePin extends Vue {
  private titleComponent = 0;

  mounted()
  {
    console.log(this.propValue);
  }

  get propValue()
  {
    return this.titleComponent;
  }

  incremnt()
  {
    this.titleComponent ++ ;
  }

}
</script>
```](url)

@SoftwareDesign-Solution
Copy link

SoftwareDesign-Solution commented Jun 25, 2021

If I omit the constructor, the error message "Uncaught (in promise) TypeError: can't convert undefined to object" does not appear. But I need the constructor because I want to load dependencies here via a container

import { Vue, Prop } from 'vue-property-decorator'
//import { container } from "tsyringe";

export default class ClassComponent extends Vue {

  @Prop({ type: String, required: true})
  private value!: string;

  constructor() {
    super();
    console.log('constructor')
  }

  onDebugClicked(): void {
    console.log('onDebugClicked');
  }

}

Installed Packages
vue 3.0.0.0
vue-class-component 8.0.0.0
vue-property-decorator 10.0.0-rc.3

@AleksandrKoltsov
Copy link

AleksandrKoltsov commented Aug 21, 2021

how can i import component in vue-class-component vue 3? @options doesn't work, i do this:

nameComponent

import { Options, Vue } from 'vue-class-component';
import nameComponent from './somePath'

@options({
components: {
nameComponent
}
})
export default class MainComponent extends Vue {}

and i get empty tag

@kid1412621
Copy link

counting on v8 official document

@MarsCaiWORD
Copy link

Hi everyone, I have a question. I tried to use Vue3 Extend to build my class base but there will be an error. Can anyone tell me what the problem is?

BaseComponent.ts
@options({})
export default class BaseComponent extends Vue {
}

MainComponent.ts
import BaseComponent from 'BaseComponent .ts';
@options({})
export default class MainComponent extends BaseComponent {
}
Error
image

@msebi
Copy link

msebi commented Nov 29, 2021

@ktsn

Please update the documentation:

https://class-component.vuejs.org/guide/class-component.html#other-options

Came by this post by mistake. It's stuff like this that keeps me (and others from donating).

Thanks!!!!

@Chris2011
Copy link

@ktsn it would be good if you can enable the "discussions" section to move those discussions to the section where it makes more sense.

@loeffel-io
Copy link

any update?

@calebeaires
Copy link

@loeffel-io, waiting as you. maybe the dont have intention to update this lib.

@JohnBernardsson
Copy link

We are already migrating every class-component to usual Object options API. Hard thing to do, but we needed to take some decision in order to update to Vue 3 ASAP...

I am a bit dissapointed though. Although I appreciate A LOT the work that has been done on this fantastic lib, I would have preferred that the communication about the future of it had been better. I waited months to take the decision pointed above, just because of this lack of clarity.

@loeffel-io
Copy link

@JohnBernardsson going with you! imo the javascript world is wild west, you can never be sure to rewrite the whole thing next week

@Chris2011
Copy link

We are already migrating every class-component to usual Object options API. Hard thing to do, but we needed to take some decision in order to update to Vue 3 ASAP...

I am a bit dissapointed though. Although I appreciate A LOT the work that has been done on this fantastic lib, I would have preferred that the communication about the future of it had been better. I waited months to take the decision pointed above, just because of this lack of clarity.

Why not migrating to the composition api? What was the reason for migrating from class components to the options API?

@charles-allen
Copy link

charles-allen commented Mar 28, 2022

I noticed this comment in the FAQ for Composition API:

Relationship with Class API

We no longer recommend using Class API with Vue 3, given that Composition API provides great TypeScript integration with additional logic reuse and code organization benefits.

I'm sad to see this -- I was a big fan of augmenting vanilla JS (fields, getters, etc) instead of learning a meta language on top -- but it seems that's not the direction the community is going (and for valid advantages).

Our team tried refactoring a couple of our components that were written with vue-class-component to Composition API, and I was at least pleasantly surprised how easy it was to migrate. And we did see the benefits of improved TypeScript support; we were able to remove a lot of explicit declarations where they are now inferred.

@Chris2011
Copy link

I noticed this comment in the FAQ for Composition API:

Relationship with Class API
We no longer recommend using Class API with Vue 3, given that Composition API provides great TypeScript integration with additional logic reuse and code organization benefits.

I'm sad to see this -- I was a big fan of augmenting vanilla JS (fields, getters, etc) instead of learning a meta language on top -- but it seems that's not the direction the community is going (and for valid advantages). Our team tried refactoring a couple of our components that were written with vue-class-component to Composition API, and I was at least pleasantly surprised how easy it was to migrate. And we did see the benefits of improved TypeScript support; we were able to remove a lot of explicit declarations where they are now inferred.

Plus the benefit of the setup attribute inside of the script tag definition to remove the boilerplate code. So at the end, I recommend to test the composition API. It is very easy and you don't need to use the data: {}, props: {} etc. if you use the setup attribute.

@micene09
Copy link

Agree with you guys, Composition API is the right way to go.
It's not easy for me to admit, not just for how much I loved the Class-Style syntax thanks to this lib, but because of the huge amount of code-base that me and my team (in my company) we have done, I'm talking about 5 years of code-base...and I'm pretty sure that there are so many companies stuck with the same problem out there.

The only thing I can suggest here, is even the only way I found to face the problem: vc2c (Vue Class 2 Composition) by yoyo930021.
It's an auto code-converter from Class Syntax to Composition API, both compatible with Vue 3 and Vue 2 compatibility plugin...you can see from the doc that it is missing some features, probably because it was a "work in progress".
Another (sad/bad) detail you can see is that it was abandoned, so I forked it and added most of the the missing features (or at least the ones that I needed), that you can notice from the commits.
Also, I've added a new method to convert every file matched by an array of glob selectors.

I know guys, I know...this cannot be the real solution, neither for me and my company's projects, but I have tried it and it can be a good place to start the conversion of big projects, instead of convert all from scratch.
Just to be clear, here is the way I'm going to face it (for every project):
Phase 1: auto-convert (using vc2c)
Phase 2: manual refactor/fix errors to make it work

I hope I have helped someone with this comment.

@serebrov
Copy link

Actually both vue-class-component and vue-property-decorator already have release candidates with Vue 3 support:

  • vue-class-component: 8.0.0-rc.1
  • vue-property-decorator: 10.0.0-rc.3

It works well for me and I was able to switch Vue 2 app to Vue 3 (even without the migration build,@vue/compat, vue-class-component serves as a bridge between vue 2 and vue 3 here).

One catch was that the base Vue class for components needs to be imported from vue-class-component, not from the vue-property-decorator (as we had it before). It works in the actual app, but fails in tests (vue-test-utils), see #524.

It was still a lot of work, since I've also upgraded other related libs - vue-router, vue-test-utils, etc, but manageable and much faster than rewriting all components.

@JohnBernardsson
Copy link

We are already migrating every class-component to usual Object options API. Hard thing to do, but we needed to take some decision in order to update to Vue 3 ASAP...
I am a bit dissapointed though. Although I appreciate A LOT the work that has been done on this fantastic lib, I would have preferred that the communication about the future of it had been better. I waited months to take the decision pointed above, just because of this lack of clarity.

Why not migrating to the composition api? What was the reason for migrating from class components to the options API?

  1. Well, Because we cannot simply stop the development while we migrate everything. We have 4 different projects of different sizes built with Vue 2. Our company (and our clients) demands functionality (which is understandable), and simply stopping everything for X number of weeks (months?) while devs perform the migration, overcomes whatever problems they found down the road, etc, is simply not an option. Migrating to the options API allows us to continue development new stuff in Vue 2 while at the same time we are preparing for a lot easier Vue 3 transition.

  2. Also because the options API is a completely direct, no-brainer migration. The team still doesn't have real/professional experience with the composition API (neither do I), so doing the mentioned above, and at the same time trying to figure our what is the best way to transform our code to correctly use / better leverage the composition API... again is not an option for us. We will migrate to the options API and then either give a chance to the composition API in specific new functionalities, or try to do some refactor on specific pieces, and see how that goes, and what we learn in the process.

At least... that is the plan! :)

@charles-allen
Copy link

charles-allen commented Apr 11, 2022

@JohnBernardsson - I'm not sure I understand your strategy. If you just want to get to Vue 3 (& not immediately to Composition), why don't you leave your code as vue-class-component syntax?

  1. Migrate to Vue 3 by adopting the latest VCC (minor syntax tweaks; much less effort than switching to OptionsAPI)
  2. Migrate to CompositionAPI whenever you extend/refactor a component

Is there some issue with Vue3/VCC that I missing? Stability?

@JohnBernardsson
Copy link

@JohnBernardsson - I'm not sure I understand your strategy. If you just want to get to Vue 3 (& not immediately to Composition), why don't you leave your code as vue-class-component syntax?

  1. Migrate to Vue 3 by adopting the latest VCC (minor syntax tweaks; much less effort than switching to OptionsAPI)
  2. Migrate to CompositionAPI whenever you extend/refactor a component

Is there some issue with Vue3/VCC that I missing? Stability?

Hey @charles-allen, some time ago I tried the latest, kind-of Vue-3-ready VCC, I don't remember right now what went wrong, but I had some issues with it. Maybe it was this, or it was when using it with vue-property-decorator (we are using them both) altogether, I don't remember clearly. There are some comments around about "gotchas" of people using those.

Also, those "latest" versions are "release candidates" at best. I cannot do a release of a webapp used by dozens of clients (a bunch of them really BIG companies) and thousands of users, with a "release candidate". I really need a production-ready state before going ahead with a migration.

@SHpiller887
Copy link

#406 (comment)
This bug is not fixed. Extends from a component causes an error. Someone solved it?

@serebrov
Copy link

@SHpiller887 Intermediate components do not work for some reason.
All components should directly extend Vue imported from vue-class-component and then it works:

import { Vue } from 'vue-class-component'
import { Options } from 'vue-property-decorator'

@Options({})
export default class MyComponent extends Vue {}

@rdhelms
Copy link

rdhelms commented Feb 20, 2023

I think @ruojianll 's vue-facing-decorator has a ton of potential...of all the alternatives to the original vue-class-component and vue-property-decorator configs that I've seen so far, that seems to work the best with our existing code with minimal changes. Has anyone else had a chance to check that out? I would love if we could come up with an official vue3 successor to these packages, since the maintainers here don't seem to be responding anymore

https://facing-dev.github.io/vue-facing-decorator/
#569 (comment)

@armano2
Copy link

armano2 commented Feb 26, 2023

I think @ruojianll 's vue-facing-decorator has a ton of potential...

there is few thinks missing but we def can make it work, i will most likely spend some time to help folks over there to fix bugs

currently atleast in typescript decorators can't affect type of what they are decorating, and that will change with ts5

https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#writing-well-typed-decorators

@zhennann
Copy link

zhennann commented May 27, 2024

Hello everyone, I have created a vue3 framework with ioc container, named as Cabloy-Front.

You can find it here: https://github.com/cabloy/cabloy-front

Do you agree with such a concept: Class should not be used in the view layer, but in the business layer.

Therefore, it is completely different from vue-facind-decorator or vue-class-component that the Vue3 setup syntax sugar is still used to define vue components (such as props and emits of components), and ioc container and class are introduced only at the business level. In this way, combine the advantages of the two (function/class) to make the code more concise and elegant.

Cabloy-Front has a very important feature: With the support of ioc container, defining reactive states no longer needs ref/reactive. Without ref, naturally there is no need to write a lot of ref.value.

Demonstration: no ref/reactive, no ref.value

Edit zhennann/cabloy-front-demo-codesandbox/main

1. Define reactive state

Define a reactive variable count in the component and add two methods to modify its value

export class MotherPageCounter {
  count: number = 0;

  inrement() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

2. Use reactive state

Use count in render class

export class RenderPageCounter {
  render() {
    return (
      <div>
        <div>count(ref): {this.count}</div>
        <button onClick={() => this.inrement()}>Inrement</button>
        <button onClick={() => this.decrement()}>Decrement</button>
      </div>
    );
  }
}

Demonstration: dependency injection

Edit zhennann/cabloy-front-demo-codesandbox/main

1. Logic Reuse

Create a Counter Bean to implement the logic of count

@Local()
export class Counter {
  count: number = 0;

  inrement() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

2. Inject and use in a component

export class MotherPageCounter {
  @Use()
  $$counter: Counter;
}
export class RenderPageCounter {
  render() {
    return (
      <div>
        <div>count(ref): {this.$$counter.count}</div>
        <button onClick={() => this.$$counter.inrement()}>Inrement</button>
        <button onClick={() => this.$$counter.decrement()}>Decrement</button>
      </div>
    );
  }
}

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

No branches or pull requests