8000 address inheritance and private fields · vuejs/rfcs@8be245e · GitHub
[go: up one dir, main page]

Skip to content

Commit 8be245e

Browse files
committed
address inheritance and private fields
1 parent 112c6ed commit 8be245e

File tree

1 file changed

+52
-49
lines changed

1 file changed

+52
-49
lines changed

active-rfcs/0000-class-api.md

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ Introduce built-in support for authoring components as native ES2015 classes.
99

1010
# Basic example
1111

12-
## In Browser
13-
1412
``` js
15-
class App extends Vue {
13+
import Vue from 'vue'
14+
15+
export default class App extends Vue {
1616
// options declared via static properties (stage 3)
1717
// more details below
1818
static template = `
19-
<div @click="increment">
20-
{{ count }} {{ plusOne }}
21-
</div>
19+
<div>{{ count }}</div>
2220
`
2321

2422
// reactive data declared via class fields (stage 3)
@@ -42,44 +40,6 @@ class App extends Vue {
4240
}
4341
```
4442

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-
import Vue from 'vue'
59-
import Foo from './Foo.vue'
60-
61-
export default class App extends Vue {
62-
static components = {
63-
Foo
64-
}
65-
66-
count = 0
67-
68-
created() {
69-
console.log(this.count)
70-
}
71-
72-
get plusOne() {
73-
return this.count + 1
74-
}
75-
76-
increment() {
77-
this.count++
78-
}
79-
}
80-
</script>
81-
```
82-
8343
# Motivation
8444

8545
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> {
393353
}
394354
```
395355

356+
357+
## Inheritance
358+
359+
Class inheritance works as expected:
360+
361+
``` js
362+
class A extends Vue {}
363+
class B extends A {}
364+
class C extends B
365+
```
366+
367+
This is equivalent to the following in Vue 2:
368+
369+
``` js
370+
const A = Vue.extend({})
371+
const B = A.extend({})
372+
const C = 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+
396377
## Mixins
397378

398379
Mixins work a bit differently with classes, primarily to ensure proper type inference:
399380

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.
401382

402383
2. To use mixins, the final component should extend a class created from the `mixins` method instead of the base `Vue` class.
403384

@@ -419,6 +400,8 @@ class MyComponent extends mixins(MixinA, MixinB) {
419400

420401
The class returned from `mixins` also accepts the same generics arguments as the base `Vue` class.
421402

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+
422405
## Difference from 2.x Constructors
423406

424407
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 {
481464

482465
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.
483466

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+
class Foo extends Vue {
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+
class Foo extends Vue {
484+
#count = 0
485+
created() {
486+
this.$self.#count // confirmed to work in Chrome 73
487+
}
488+
}
489+
```
490+
484491
## Two Ways of Doing the Same Thing
485492

486493
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
489496

490497
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.
491498

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.
495-
496499
# Alternatives
497500

498501
## Options via Decorator

0 commit comments

Comments
 (0)
0