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

[Abandoned] Class API proposal #17

Closed
wants to merge 10 commits into from
Closed

[Abandoned] Class API proposal #17

wants to merge 10 commits into from

Conversation

yyx990803
Copy link
Member

@yyx990803 yyx990803 commented Feb 27, 2019

Rendered

Update: this proposal has been dropped. Read why here.

@yyx990803 yyx990803 added 3.x This RFC only targets 3.0 and above core labels Feb 27, 2019
@ashokgelal
Copy link

What about local component registration?

Also, I might start using TypeScript after this :)

@yyx990803
Copy link
Member Author

@ashokgelal any existing option can be mapped to a static property:

class Foo extends Vue {
  static components = {
    Bar,
    Baz
  }
}

@ashokgelal
Copy link

@yyx990803 That's what I thought. And does that mean with Typescript it would be something like @components({ ... })

It'd be nice to mention local component registration in the RFC as well.

@crutchcorn
Copy link

In regards to using [[Set]], I could easily see this being a large point of confusion, especially for those coming from other front-end component libraries where this behavior does not cause problems. Might it be a good idea to mention a warning of some kind in the parent Vue class if this is done? I'm not even sure that's the right way to go but I'm moreover noting that I personally would have had a fair amount of confusion had not read this RFC previously

@yyx990803
Copy link
Member Author

yyx990803 commented Feb 27, 2019

@ashokgelal no, you do that the same way in TypeScript. @prop is the only decorator that we currently plan to include (for the reason discussed in the decorators section)

@crutchcorn the note on [[Set]] vs [[Define]] is just there for those who are interested in the details (mostly library authors). In practice it doesn't really "cause problems" of any kind - users don't need to be aware of this.

@CyberAP
Copy link
Contributor

CyberAP commented Feb 27, 2019

How would inject/provide look like?

@jaesung2061
Copy link

jaesung2061 commented Feb 27, 2019

Would we need to us TS if we wanted prop validation?

Edit: Nevermind, dumb question.

@aparajita
Copy link

Would this work in the script section of an SFC? Don't especially want to give up all of the advantages of having a separate <template> section vs. a string.

@yyx990803
Copy link
Member Author

@aparajita of course!

@aparajita
Copy link

@yyx990803 Well, your first "basic usage" example uses a template string, and none of the examples show usage within an SFC, so I hope you can see why I was wondering. It's obvious to you, but not necessarily to everyone else. The more explicit you are in docs, the better.

In any case, I would suggest that the overwhelming "basic usage" will be in an SFC, not using a template string, and perhaps the first example should be changed accordingly.

@twaite
Copy link

twaite commented Feb 28, 2019

So it sounds like based on the Alternatives section that you're going to be using decorators, is there any way we could something like this to make it simpler to keep our interfaces in one spot without generics?

interface propType {
  numeric: number,
  text: string,
}

class MyComponent extends Vue {
  @props()
  props: propType = {
    numeric: 0,
    text: 'string',
  }

  created() {
    this.msg
  }
}

I guess this doesn't solve the issue of validators. But I wish that I could create one interface for props and one for my data. that keeps the different concerns isolated.

@sombriks
Copy link

as long as the old object notation remains valid, this is perfect.

@brianjohnsonsr
Copy link

I'm sold on it, given the last paragraph in the adoption strategy. As long as the vue-class-component API can remain the same while the implementation is improved, I see no issues.

@Justineo
Copy link
Member

Should component inheritance be covered in the RFC? Like how one component extends another one, possibly with mixins included at the same time.

@634750802
Copy link

Hi, I have some questions:

  1. How could I declare a non-observable field?
  2. How to create a method that has the same name with reserved name?

For question 1, maybe vue can provide a decorator @Observable() or @NonObservable().

For question 2, maybe vue can provide some special constant key to declare a hook. For example:

import Vue, { Options, Lifecycles } from 'vue'

class Component extends Vue {

    [Options.provide] () {
        return { bar: 'foo' }
    }

    [Options.data] () {
        return { foo: 'bar' }
    }

    [Lifecycles.created] () { 
        // ... 
    }

}

@caridy
Copy link

caridy commented Feb 28, 2019

Note about mixins, we are trying to align multiple frameworks to provide sugar for the current stage 1 proposal for mixins, and so far, people seems to like it. Have you consider using:

class Foo extends mix(Bar).with(X, Y) { 
    ...
}

The implementation of that utility is quite simple, and in maps exactly to the syntax that we are proposal for the language:

class MixinBuilder {
    constructor(superclass) {
        this.superclass = superclass;
    }

    // This is a method of the class, not a "with" statement
    with(...mixins) {
        return mixins.reduce((c, mixin) => mixin(c), this.superclass);
    }
}

export default superclass => new MixinBuilder(superclass);

@alexsasharegan
Copy link

What distinguishes a method from a filter?

@GlebkaF
Copy link

GlebkaF commented Feb 28, 2019

What about checking prop type in template?
It would be great if Vue could leverage typescript to check prop types :) Any plans?

@michaelolof
Copy link

Guessing watchers will also be defined via decorators.

@zuoez02
Copy link

zuoez02 commented Feb 28, 2019

Which will be called first? constructor() or beforeCreate()? Currently beforeCreate will be called first.

import Vue from 'vue';
import Component from 'vue-class-component';

@Component
class App extends Vue {
  constructor() {
    super();
    console.log('constructor');
  }

  beforeCreate() {
    console.log('beforeCreate');
  }

  created() {
    console.log('created');
  }
}

new App();

// beforeCreate
// constructor
// created

image

@zaun
Copy link

zaun commented Feb 28, 2019

Could you remove the need the prop decorator if props were only available via this.$props? I don’t see a need for them to be directly on this like a component’s data is. In fact it makes it clear your accessing a prop rather than data.

Also, how do you go about defining what the types are for this.$refs? Im having to cast them currently and would like to avoid that in the future if at all possible. Ideally if you try to access a $refs property that’s not setup with a ref attribute in the template it should error.

@donnysim
Copy link

donnysim commented Feb 28, 2019

I also like the idea of only having this.$props and this.$data, no direct access on this. This also makes a clear distinction that this.isLoggedIn is a computed property, and this.success() is a class methods instead of:

data() {
  return {
    success: () => { /* ... */},
  };
},

or

props: {
  success: {
    type: Function,
  },
},

being accessed through this.success() where prop, data and class method can exist with the same name.

There also is a need for non-reactive properties, like storing Popper instance, without having it trigger useless updates.

@rickyruiz
Copy link

The RFC does not mention anything about Vue.extend explicitly. As a TypeScript user, I've never needed vue-class-component.

  • Using classes will be the recommended option for TypeScript users?
  • Are there going to be any disadvantages if I keep using Vue.extend instead of classes?

@zuoez02
Copy link

zuoez02 commented Feb 28, 2019

@rickyruiz This is Class API proposal

@smolinari
Copy link
Contributor

@kabaluyot - From what has been said in the past by the Vue team, yes. But, you'll hopefully learn at some point that it is the lesser pragmatic use of Vue. 😀

Scott

@jaytonic
Copy link

jaytonic commented May 6, 2020

Really sad about this, the class approach with decorator was so easy to read, no more weird "declare an object with a property named computed that contains a method to just have something that is implemented in base javascript with getters :(.

I was really hyped by this until I found out it was going to be dropped. It would have been perfect for smaller projects than my angular ones.

@pikax
Copy link
Member

pikax commented May 6, 2020

There will be support through vue-class-component

@jaytonic
Copy link

jaytonic commented May 6, 2020

There will be support through vue-class-component

Still mean that this will not be present in web courses, not in base documentation, less community :(
I cannot understand this choice. This approach was doing a lot of the composition api is doing be in a more OOP way.

Wish they kept that(and added service injections for stuff like ionic)

@kabaluyot
Copy link

Actually the main thing that im hoping tha the vue-class-component still be supported officially is the transition when the time comes when the composition API matures and had advantages in real application compared to class components.

Also, the class component will have a bridge to composition APIs. We have assurance with the vue-class-component author: vuejs/vue-class-component#402

@alexey2baranov
Copy link

alexey2baranov commented Jun 6, 2020

Sorry if my question duplicates others, but I really want to ask Evan.

What was the main reason when he was preferring the Functional API over the Class API. Has the Functional API won because of current JS/TS limitations (lack of JS decorators, TS typing limitations, etc... ) or because of functional style is more awesome?

BTW: could't open "read why" link on top

@smolinari
Copy link
Contributor

@alexey2baranov - #17 (comment)

Scott

@John0King
Copy link

John0King commented Jun 8, 2020

this is nothing wrong in Typescript, and it just not work for previous "re-project-this" base transformer and I previous write a prototype of typescript runtime for wechat-mini-programe and the this is the real this, instead of the hacker one

https://github.com/John0King/typed-we-app/blob/705b2a128de3e846fa31c5fc620005aa62f5a72b/runtime/Runtime.ts#L181-L241

from what I understand,

export default {  // this is a object  which mean this is an instance of class,  only the class is Object
    setup() {
      const state = reactive({
        count: 0,
        double: computed(() => state.count * 2), 
      })

      function increment() {
        state.count++
      }

      return {  //  again  this is another instance of Object, 
        state,
        increment,
      }
    },
  }

let me transform you to typescript class word

class MyConfigurate{
    state : any =  reactive({
        count: 0,
        double: computed(() => this.state.count * 2), 
      }) ;// sorry I do not know want type it is, so I'm using AnyScript 🤣
     increment() : void {
        this.state.count++
      } // if this `this` is the problem, then use 
      increment2:()=>void = ()=>{
        this.state.count++;
     }
}
class MyComponent{
    setup() {
         return new MyConfigurate();
    }
}

export default new MyComponent();

so you can see, there are nothing wrong with this right ?
the only difference is that the prototype of MyConfigurate and MyCompont is not same as Object, but I do not think the class.prototype is the blocker here.

and my prototype being abandoned previously because it can not find a proper time to attach the projected this , you can see I attach the this from the begin of the Page lifetime OnLoad , but it also too late, so I doing lot of the work to delay the property initialization after I attach to the this .
but it doen't has any issue for Vue 3, because Vue3's root Object (eg. MyComponent) has a setup method , and it indeed has two level of object 😀


after use the decorator it should be look like this

@Component()
export default class Hellowork{
    count = 0;
    get double(){
        return this.count * 2
    }
    increment() : void {
        this.state.count++
      } 
    // if use some reused function
   position :{ x:number, y:number } = useMousePosition() 
    // or
    constructor()
   {
      var { x, y } = useMousePosition();
     this.x = x;
      this.y = y;
    }
    x:number;
    y:number;

    //inject

   @inject(()=>useStore())
    store:any!;
}

the Component decorator do 2 things,

  1. it wrap this class with a root object with setup function
  2. it apply necessary change to this class and return in the setup function

@sreenaths
Copy link

sreenaths commented Mar 6, 2021

[composition class] Component setup using ES6 class works out of the box in Vue 3, why not officially support it!
discussions @ #276

@lzxb
Copy link

lzxb commented Aug 4, 2022

I implemented a simple scheme.
Use class to write setup, and support vue2 and vue3
https://github.com/fmfe/vue-class-setup

@hanzhangyu
Copy link

hanzhangyu commented Aug 4, 2022 via email

@ruojianll
Copy link

I like class base syntax, may be https://github.com/facing-dev/vue-facing-decorator 's implementation could give us some inspirations.

@hybridwebdev
Copy link

hybridwebdev commented Dec 12, 2022

I implemented a simple scheme. Use class to write setup, and support vue2 and vue3 https://github.com/fmfe/vue-class-setup

I don't see how this is, in any way, an improvement. Nor do I see any real world use cases for this. This doesn't seem relevant to the discussion, it comes across as pointless self-promotion.

@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>
    );
  }
}

@cztomsik
Copy link

Business layer is typically one layer below the backend API. I think you should use some other term for whatever you are proposing (maybe presenter from MVP? or VM from MVVM?)

@hybridwebdev
Copy link

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>
    );
  }
}

This is not related to this now LONG CLOSED issue. This is spam. This is not the place to post advertisements for your repos.

@zhennann
Copy link

Business layer is typically one layer below the backend API. I think you should use some other term for whatever you are proposing (maybe presenter from MVP? or VM from MVVM?)

The business layer can be understood as various types of resources related to business, including models, presenter, style, and so on. These can be encapsulated as class beans to hosted in the ioc container, so that it is easier to share the business -related data and logic.

@zhennann
Copy link

@hybridwebdev Cabloy-Front is a new idea for vue3+tsx+ioc, and introduces a different new dx. So maybe no problem taking it out to share with community.
The reason why this issue is closed is that Vue Team does not recommend the Class API. Their reason is that it is inconvenient to define Vue components using Class, code redundancy, and not cost-effective. I also agree with this. The community has several solutions based on Class to define Vue components, and the effect is not ideal. This illustrates a truth: Class should not be used in the view layer, but in the business layer. Therefore, Cabloy-Front still uses Setup syntax sugar to define components (including props and emits) in the view layer, and introduce ioc containers and class in the business layer. With the support of ioc container, defining reactive states no longer needs ref/reactive, nor ref.value

@cztomsik
Copy link

cztomsik commented May 28, 2024

The business layer can be understood

My point was that some people might be puzzled about what you mean (I certainly was). Just because you use DI does not mean that you also need to adopt all the naming conventions. Because SPA is presentational layer, and if you zoom out, then you will have 2 business layers (SPA + backend) in the whole picture, and it's weird. Just my 2 cents.

@zhennann
Copy link

From a full-stack perspective, I agree with you that the business layer refers to the backend API. But if you just look at it from a front-end perspective, in a large project, there is a concept of business layer.

@hybridwebdev
Copy link

@hybridwebdev Cabloy-Front is a new idea for vue3+tsx+ioc, and introduces a different new dx. So maybe no problem taking it out to share with community. The reason why this issue is closed is that Vue Team does not recommend the Class API. Their reason is that it is inconvenient to define Vue components using Class, code redundancy, and not cost-effective. I also agree with this. The community has several solutions based on Class to define Vue components, and the effect is not ideal. This illustrates a truth: Class should not be used in the view layer, but in the business layer. Therefore, Cabloy-Front still uses Setup syntax sugar to define components (including props and emits) in the view layer, and introduce ioc containers and class in the business layer. With the support of ioc container, defining reactive states no longer needs ref/reactive, nor ref.value

You don't seem to be grasping what I am saying. You are spamming. This is not the place to advertise your product. Stop.

@vuejs vuejs locked and limited conversation to collaborators May 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
3.x This RFC only targets 3.0 and above core
Projects
None yet
Development

Successfully merging this pull request may close these issues.