Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vuejs/vue-class-component
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v7.2.2
Choose a base ref
...
head repository: vuejs/vue-class-component
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Loading
29 changes: 29 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Contributing to Vue Class Component

Thank you for contributing to Vue Class Component! To manage the process smoothly, please take a look at the following points.

## Issue Reporting

Please make sure that your issue is either a **bug report** or **feature request**. The other kinds of issue will be closed immediately. For usage questions,
please use [Stack Overflow](https://stackoverflow.com/), [the official Vue.js forum](https://forum.vuejs.org), [the official Discord server](https://chat.vuejs.org), etc.

Also, please try searching existing issue before creating a new one. Your issue may exist already in an old thread.

### Bug Report

Please **make sure to provide [minimal and self-contained reproduction](https://new-issue.vuejs.org/?repo=vuejs/vue#why-repro)**.
A bug report without a proper reproduction may be closed immediately.

To create a reproduction, you can use on-browser playground such as JSFiddle ([template for Vue Class Component](https://jsfiddle.net/ktsn/nm55jnjk/))
or create a GitHub repository and share its link.

Please also clarify the expected behavior, actual behavior and steps to reproduce the bug to confirm it precisely.

### Feature Request

For feature request, please make sure to clarify **the use case** that you want to solve.
Explaining it with actual code example or pseudo code would be useful to share your idea.

## Pull Request

Please do not commit the files under `dist/`. They are supporsed to be generated during the releasing process.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules
example/build.js
example/build.js.map
docs/.vuepress/dist
test/test.build.js
lib
15 changes: 15 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
tasks:
- name: Compile
init: yarn install && yarn build && gp sync-done install
command: yarn dev
- name: Docs
init: gp sync-await install
command: yarn docs:dev

ports:
- port: 8080
onOpen: open-preview

vscode:
extensions:
- octref.vetur@0.23.0:TEzauMObB6f3i2JqlvrOpA==
315 changes: 16 additions & 299 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,317 +1,34 @@
# vue-class-component
# [DEPRECATED] Vue Class Component

> ECMAScript / TypeScript decorator for class-style Vue components.

[![npm](https://img.shields.io/npm/v/vue-class-component.svg)](https://www.npmjs.com/package/vue-class-component)
## ⚠️ Notice

### Usage
This library is no longer actively maintained. It is no longer recommend to use Class-based components in Vue 3. The recommended way to use Vue 3 in large applications is Single-File Components, Composition API, and `<script setup>`. If you still want to use classes, check out the community-maintained project [`vue-facing-decorator`](https://facing-dev.github.io/vue-facing-decorator/#/).

**Required**: [ECMAScript stage 1 decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md).
If you use Babel, [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) is needed.
If you use TypeScript, enable `--experimentalDecorators` flag.
Additionally, if you're interested in migrating out of class components, you might find the CLI tool [`vue-class-migrator`](https://github.com/getyourguide/vue-class-migrator) helpful for the transition.

> It does not support the stage 2 decorators yet since mainstream transpilers still transpile to the old decorators.
---

Note:
ECMAScript / TypeScript decorator for class-style Vue components.

1. `methods` can be declared directly as class member methods.
[![npm](https://img.shields.io/npm/v/vue-class-component.svg)](https://www.npmjs.com/package/vue-class-component) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/vuejs/vue-class-component)

2. Computed properties can be declared as class property accessors.
## Document

3. Initial `data` can be declared as class properties ([babel-plugin-transform-class-properties](https://babeljs.io/docs/plugins/transform-class-properties/) is required if you use Babel).
See [https://class-component.vuejs.org](https://class-component.vuejs.org)

4. `data`, `render` and all Vue lifecycle hooks can be directly declared as class member methods as well, but you cannot invoke them on the instance itself. When declaring custom methods, you should avoid these reserved names.
> Please note, documentation for v8 is not ready yet. Check out the readme in the respective branch or see [v8 proposals in the issue list](https://github.com/vuejs/vue-class-component/issues?q=is%3Aopen+is%3Aissue+label%3Av8)
5. For all other options, pass them to the decorator function.
## Online one-click setup for contributing

### Example
Contribute to Vue Class Component using a fully featured online development environment that will automatically: clone the repo, install the dependencies and start the docs web server and run `yarn dev`.

Following is the example written in Babel. If you are looking for TypeScript version, [it's in the example directory](example/src/App.vue).
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/)

``` vue
<template>
<div>
<input v-model="msg">
<p>prop: {{propMessage}}</p>
<p>msg: {{msg}}</p>
<p>helloMsg: {{helloMsg}}</p>
<p>computed msg: {{computedMsg}}</p>
<button @click="greet">Greet</button>
</div>
</template>
## Issue reporting / pull requests

<script>
import Vue from 'vue'
import Component from 'vue-class-component'
See [contribution guideline](./.github/CONTRIBUTING.md)

@Component({
props: {
propMessage: String
}
})
export default class App extends Vue {
// initial data
msg = 123
// use prop values for initial data
helloMsg = 'Hello, ' + this.propMessage
// lifecycle hook
mounted () {
this.greet()
}
// computed
get computedMsg () {
return 'computed ' + this.msg
}
// method
greet () {
alert('greeting: ' + this.msg)
}
}
</script>
```

You may also want to check out the `@prop` and `@watch` decorators provided by [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator).

### Using Mixins

vue-class-component provides `mixins` helper function to use [mixins](https://vuejs.org/v2/guide/mixins.html) in class style manner. By using `mixins` helper, TypeScript can infer mixin types and inherit them on the component type.

Example of declaring a mixin:

``` js
// mixin.js
import Vue from 'vue'
import Component from 'vue-class-component'

// You can declare a mixin as the same style as components.
@Component
export default class MyMixin extends Vue {
mixinValue = 'Hello'
}
```

Example of using a mixin:

``` js
import Component, { mixins } from 'vue-class-component'
import MyMixin from './mixin.js'

// Use `mixins` helper function instead of `Vue`.
// `mixins` can receive any number of arguments.
@Component
export class MyComp extends mixins(MyMixin) {
created () {
console.log(this.mixinValue) // -> Hello
}
}
```

### Create Custom Decorators

You can extend the functionality of this library by creating your own decorators. vue-class-component provides `createDecorator` helper to create custom decorators. `createDecorator` expects a callback function as the 1st argument and the callback will receive following arguments:

- `options`: Vue component options object. Changes for this object will affect the provided component.
- `key`: The property or method key that the decorator is applied.
- `parameterIndex`: The index of a decorated argument if the custom decorator is used for an argument.

Example of creating `NoCache` decorator:

``` js
// decorators.js
import { createDecorator } from 'vue-class-component'

export const NoCache = createDecorator((options, key) => {
// component options should be passed to the callback
// and update for the options object affect the component
options.computed[key].cache = false
})
```

``` js
import { NoCache } from './decorators'

@Component
class MyComp extends Vue {
// the computed property will not be cached
@NoCache
get random () {
return Math.random()
}
}
```

### Adding Custom Hooks

If you use some Vue plugins like Vue Router, you may want class components to resolve hooks that they provide. For that case, `Component.registerHooks` allows you to register such hooks:

```js
// class-component-hooks.js
import Component from 'vue-class-component'

// Register the router hooks with their names
Component.registerHooks([
'beforeRouteEnter',
'beforeRouteLeave',
'beforeRouteUpdate' // for vue-router 2.2+
])
```

```js
// MyComp.js
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
class MyComp extends Vue {
// The class component now treats beforeRouteEnter
// and beforeRouteLeave as Vue Router hooks
beforeRouteEnter (to, from, next) {
console.log('beforeRouteEnter')
next() // needs to be called to confirm the navigation
}

beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave')
next() // needs to be called to confirm the navigation
}
}
```

Note that you have to register the hooks before component definition.

```js
// main.js

// Make sure to register before importing any components
import './class-component-hooks'

import Vue from 'vue'
import MyComp from './MyComp'

new Vue({
el: '#app',
components: {
MyComp
}
})
```

### Enabling Custom Hooks Auto-complete in TypeScript

vue-class-component provides a built-in hooks type, which enables auto-complete for `data`, `render` and other lifecycle hooks in class component declarations, for TypeScript. To enable it, you need to import hooks type located at `vue-class-component/hooks`.

```ts
// main.ts
import 'vue-class-component/hooks' // import hooks type to enable auto-complete
import Vue from 'vue'
import App from './App.vue'

new Vue({
render: h => h(App)
}).$mount('#app')
```

If you want to make it work with custom hooks, you can manually add it by yourself:

```ts
import Vue from 'vue'
import { Route, RawLocation } from 'vue-router'

declare module 'vue/types/vue' {
// Augment component instance type
interface Vue {
beforeRouteEnter?(
to: Route,
from: Route,
next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void
): void

beforeRouteLeave?(
to: Route,
from: Route,
next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void
): void

beforeRouteUpdate?(
to: Route,
from: Route,
next: (to?: RawLocation | false | ((vm: Vue) => any) | void) => void
): void
}
}
```

### Caveats of Class Properties

vue-class-component collects class properties as Vue instance data by instantiating the original constructor under the hood. While we can define instance data like native class manner, we sometimes need to know how it works.

#### `this` value in property

If you define an arrow function as a class property and access `this` in it, it will not work. This is because `this` is just a proxy object to Vue instance when initializing class properties:

```js
@Component
class MyComp extends Vue {
foo = 123

bar = () => {
// Does not update the expected property.
// `this` value is not a Vue instance in fact.
this.foo = 456
}
}
```

You can simply define a method instead of a class property in that case because Vue will bind the instance automatically:

```js
@Component
class MyComp extends Vue {
foo = 123

bar () {
// Correctly update the expected property.
this.foo = 456
}
}
```

#### `undefined` will not be reactive

To take consistency between the decorator behavior of Babel and TypeScript, vue-class-component does not make a property reactive if it has `undefined` as initial value. You should use `null` as initial value or use `data` hook to initialize `undefined` property instead.

```js
@Component
class MyComp extends Vue {
// Will not be reactive
foo = undefined

// Will be reactive
bar = null

data () {
return {
// Will be reactive
baz: undefined
}
}
}
```

### Build the Example

``` bash
$ npm install && npm run example
```

### Questions

For questions and support please use the [the official forum](http://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests.

### License
## License

[MIT](http://opensource.org/licenses/MIT)
Loading