You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: active-rfcs/0000-class-api.md
+52-49Lines changed: 52 additions & 49 deletions
Original file line number
Diff line number
Diff line change
@@ -9,16 +9,14 @@ Introduce built-in support for authoring components as native ES2015 classes.
9
9
10
10
# Basic example
11
11
12
-
## In Browser
13
-
14
12
```js
15
-
classAppextendsVue {
13
+
importVuefrom'vue'
14
+
15
+
exportdefaultclassAppextendsVue {
16
16
// options declared via static properties (stage 3)
17
17
// more details below
18
18
static template =`
19
-
<div @click="increment">
20
-
{{ count }} {{ plusOne }}
21
-
</div>
19
+
<div>{{ count }}</div>
22
20
`
23
21
24
22
// reactive data declared via class fields (stage 3)
@@ -42,44 +40,6 @@ class App extends Vue {
42
40
}
43
41
```
44
42
45
-
The component will be mounted using a new global API instead of `new Vue()` - this will be discussed in a separate RFC.
46
-
47
-
## In Single File Components
48
-
49
-
```html
50
-
<template>
51
-
<div@click="increment">
52
-
{{ count }} {{ plusOne }}
53
-
<Foo />
54
-
</div>
55
-
</template>
56
-
57
-
<script>
58
-
importVuefrom'vue'
59
-
importFoofrom'./Foo.vue'
60
-
61
-
exportdefaultclassAppextendsVue {
62
-
static components = {
63
-
Foo
64
-
}
65
-
66
-
count =0
67
-
68
-
created() {
69
-
console.log(this.count)
70
-
}
71
-
72
-
getplusOne() {
73
-
returnthis.count+1
74
-
}
75
-
76
-
increment() {
77
-
this.count++
78
-
}
79
-
}
80
-
</script>
81
-
```
82
-
83
43
# Motivation
84
44
85
45
Vue's current object-based component API has created some challenges when it comes to type inference. As a result, most users opting into using Vue with TypeScript end up using [vue-class-component](https://github.com/vuejs/vue-class-component). This approach works, but with some drawbacks:
@@ -393,11 +353,32 @@ class MyComponent extends Vue<MyProps, MyData> {
393
353
}
394
354
```
395
355
356
+
357
+
## Inheritance
358
+
359
+
Class inheritance works as expected:
360
+
361
+
```js
362
+
classAextendsVue {}
363
+
classBextendsA {}
364
+
classCextendsB
365
+
```
366
+
367
+
This is equivalent to the following in Vue 2:
368
+
369
+
```js
370
+
constA=Vue.extend({})
371
+
constB=A.extend({})
372
+
constC=B.extend({})
373
+
```
374
+
375
+
However, direct inheritance is not very useful when it comes to UI components, as it only handles linear inheritance of logic and does not cover composition in the rendering output. It is therefore not recommended to rely heavily on class inheritance. Instead, prefer composition via mixins and slots.
376
+
396
377
## Mixins
397
378
398
379
Mixins work a bit differently with classes, primarily to ensure proper type inference:
399
380
400
-
1. If type inference is needed, mixins must be declared as classes extending the base `Vue` class (otherwise, the object format also works).
381
+
1. If type inference is needed, mixins must be declared as classes extending the base `Vue` class (otherwise, the object format also works). The fact that classes can be used as mixins also means any existing component can be used as a mixin.
401
382
402
383
2. To use mixins, the final component should extend a class created from the `mixins` method instead of the base `Vue` class.
403
384
@@ -419,6 +400,8 @@ class MyComponent extends mixins(MixinA, MixinB) {
419
400
420
401
The class returned from `mixins` also accepts the same generics arguments as the base `Vue` class.
421
402
403
+
There is also a [stage-1 proposal for native mixin syntax](https://github.com/justinfagnani/proposal-mixins) which is essentially syntax sugar for nested inheritance. Vue 3 should be able to work out of the box with that.
404
+
422
405
## Difference from 2.x Constructors
423
406
424
407
One major difference between 3.0 classes and the 2.x constructors is that they are not meant to be instantiated directly. i.e. you will no longer be able to do `new MyComponent({ el: '#app' })` to mount it - instead, the instantiation/mounting process will be handled by separate, dedicated APIs. In cases where a component needs to be instantiated for testing purposes, corresponding APIs will also be provided. This is largely due to the internal changes where we are moving the mounting logic out of the component class itself for better decoupling, and also has to do our plan to redesign the global API for bootstrapping an app.
@@ -481,6 +464,30 @@ class MyComponent extends Vue {
481
464
482
465
In practice, there shouldn't be cases where you must use the `constructor`, so the best practice is to simply avoid it and always use component lifecycle hooks.
483
466
467
+
## Usage with Private Fields
468
+
469
+
Due to `this` being Proxies, this API won't work directly with [Private Class Fields](https://github.com/tc39/proposal-private-methods) (stage 3 proposal), because private fields by design are not exposed on Proxies of the original instance. So the following will not work:
470
+
471
+
```js
472
+
classFooextendsVue {
473
+
#count =0
474
+
created() {
475
+
this.#count // Error because `this` is a Proxy
476
+
}
477
+
}
478
+
```
479
+
480
+
This can be worked around by exposing the raw, original instance as a speical property (naming tentative):
481
+
482
+
```js
483
+
classFooextendsVue {
484
+
#count =0
485
+
created() {
486
+
this.$self.#count // confirmed to work in Chrome 73
487
+
}
488
+
}
489
+
```
490
+
484
491
## Two Ways of Doing the Same Thing
485
492
486
493
This may cause beginners to face a choice early on: to go with the object syntax, or the class syntax?
@@ -489,10 +496,6 @@ For users who already have a preference, it is not really an issue. The real iss
489
496
490
497
One way to deal with it is providing examples for both syntaxes in the new docs and allow switching between them. This allows users to pick a preferred syntax during the learning process.
491
498
492
-
## Isn't React Moving Away from Classes?
493
-
494
-
Yes, but that's because classes isn't as nicely a fit for React's component conceptual model, especially with hooks being the alternative. Classes aren't inherently bad, and Vue's component conceptual model (with mutable state) maps better to a class than a React component. We also have plans to offer a mechanism with hooks-like logic composition capabilities that works in both class and object-based components, but that will be in a separate future RFC.
0 commit comments