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

a few suggestions from someone using Vue + TypeScript every day for the last six months #4

Open
louisreingold opened this issue Aug 13, 2020 · 7 comments

Comments

@louisreingold
Copy link

louisreingold commented Aug 13, 2020

  1. Use class-based components, not Vue's proprietary data shape that gets passed to Vue.extend. (at least for Vue 2)

  2. For Vuex support (types on your action / mutation payloads), use https://github.com/championswimmer/vuex-module-decorators.

  3. Put "vetur.experimental.templateInterpolationService": true in your VS Code settings to get TypeScript analysis on your <template> in .vue files. This only works inside VS Code with Vetur installed - vue-cli-service build won't show you type errors in your <template>.

  4. Don't forget that @someEvent='someEventHandler' isn't type safe. This is the only major area where Vue and TypeScript don't play nice and you end up with a black hole of untyped data coming into your application.

  5. Unrelated to Vue - verify the data coming into your app using IO-TS: https://github.com/gcanti/io-ts

  6. For linting, use ESLint + Prettier. Rather than wrestling with the setup, just use the Vue CLI (or run vue ui) and then choose to add TypeScript support to your project when initially creating it and choose ESLint + Prettier. @ts-ignore is banned by default. To change that, modify your eslintConfig.rules in your package.json file. Here's mine:

  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended",
      "@vue/typescript/recommended",
      "@vue/prettier",
      "@vue/prettier/@typescript-eslint"
    ],
    "parserOptions": {
      "parser": "@typescript-eslint/parser",
      "ecmaVersion": 2020
    },
    "rules": {
      "no-debugger": 1,
      "@typescript-eslint/ban-ts-ignore": 0,
      "no-var": 1,
      "@typescript-eslint/no-array-constructor": 1,
      "@typescript-eslint/camelcase": 1,
      "@typescript-eslint/no-use-before-define": 0,
      "prefer-const": 1,
      "@typescript-eslint/no-inferrable-types": 1,
      "@typescript-eslint/no-this-alias": 1,
      "@typescript-eslint/no-unused-vars": 1,
      "@typescript-eslint/no-explicit-any": 0,
      "@typescript-eslint/no-empty-function": 0,
      "no-console": 0,
      "@typescript-eslint/no-non-null-assertion": 0
    }
  }
@louisreingold louisreingold changed the title a few suggestions from someone using Vue + TypeScript every day for almost a year a few suggestions from someone using Vue + TypeScript every day for the last six months Aug 13, 2020
@chiubaca
Copy link
Collaborator

Nice tips! Will find a way to incorporate this all in. I'm encountering point number 4 too, know of any good patterns to workaround this?

@swyxio
Copy link
Contributor

swyxio commented Aug 13, 2020

awesome tips louisreingold !

@Roninii
Copy link
Collaborator

Roninii commented Aug 15, 2020

Awesome advice! If you've got the time for a PR, it's more than welcome! Otherwise we'll get to it soon (:

As for point 1, I definitely think it should be noted that Vue 3 is moving away from this set up as shown here and here, and I personally wouldn't encourage anyone to use this approach.

Of course these are just my opinions and a section demonstrating this approach could still be included as an option!

@chiubaca
Copy link
Collaborator

@Roninii
I think there is a little bit of confusion between Class Components and the proposed(now abandoned) Class API. I'm still wrapping my head around all this but this is my current understanding of the whole situation.

Currently we have Vue class components - https://github.com/vuejs/vue-class-component . This is an officially maintained extension of Vue.

This is different from the Vue 3 Class API proposal - vuejs/rfcs#17 . Though very similar there are few subtle differences outlined in the RFC. This was in direct competition with the Composition API. As we now know, the Composition API won this battle. As linked above, this is Evan You's reasoning as to why it was dropped - vuejs/rfcs#17 (comment) .

However, there are no plans to deprecate the current Vue class component. In fact, there is an open issue to adapt the Vue class component for the upcoming changes for Vue 3 - vuejs/vue-class-component#406 .

Sorry I realise this is a bit a brain dump. Please correct me if I'm wrong anywhere.

I'm currently in the process of refactoring one of projects to use class components. So happy to start working on this section. I've been holding off because it seems to be a slightly polarising topic in Vue land. But in the context of using it for better Typescript support, I think it makes a lot of sense!

@Roninii
Copy link
Collaborator

Roninii commented Aug 16, 2020

I 100% agree that this should be supported and added to the cheat sheet.

My only concern is that I think the issue thread noting why the proposal was dropped, and what the drawbacks vs. the composition api are.

It will certainly always be an option for those that prefer to use it anyways, and I think that should be encouraged in something so expressive as code. I would just like to add a note that defineComponent is the preferred and official approach.

@IAMtheIAM
Copy link

IAMtheIAM commented Jan 28, 2021

I am in the process of refactoring an Options Vue 2 app into the class Component style. I was confused about Class Component and Class Api @chiubaca Thanks for clarifying the difference!

Personally, I prefer the class component syntax because it's formalized and mostly standard TypeScript. That allows for easier transitions between frameworks like Angular, Vue, React, etc.

The only thing I was wondering about is what the equivalent of setup() is from the Composition API but when usnig Class Components. But I figured it out. There was a note about not the constructor function to run initialization code for the component when using Class Components, so we can just run our initialization code in the early view lifecycle hooks such as created().

Awesome writeup @louisreingold!

To add to that

  • Originally I was using Vuex for basic state management, but now I've dumped it and switched to Akita which is way more robust and easy to use when paired with TypeDI. Now you have easy dependency injection with reactive state management.

@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
None yet
Projects
None yet
Development

No branches or pull requests

6 participants