Skip to content

Commit 2a08f34

Browse files
committed
basic support for ref, computed and class inherite
1 parent 8f7e94c commit 2a08f34

File tree

8 files changed

+230
-76
lines changed

8 files changed

+230
-76
lines changed

readme.md

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,40 @@ This repo is not released yet. Welcome to suggest and contribute. Message me on
55
# What now
66

77
```typescript
8-
import { Component ,Base} from "ThisRepo";
8+
import { Component, Ref, Base } from "ThisRepo";
9+
10+
//super class
11+
class Sup extends Base {
12+
//reactivity super property
13+
supProp = "supProp";
14+
//super method
15+
supMethod() {}
16+
//super getter
17+
get supGetter() {
18+
return "supFoo";
19+
}
20+
}
21+
22+
//component class
923
@Component
10-
export default class MyComponent extends Base{
11-
prop = "p";//a property
12-
meth() {//a method
13-
this.$forceUpdate()//call vue api
24+
export default class Comp extends Sup {
25+
//create a ref
26+
@Ref
27+
ref!: HTMLDivElement;
28+
//reactivity property
29+
prop = "prop";
30+
//getter
31+
get getter() {
32+
return "foo";
1433
}
15-
mounted(){//vue lifecycle hook
16-
console.log('foo')
34+
//method
35+
method() {
36+
//call vue api
37+
this.$forceUpdate();
38+
}
39+
mounted() {
40+
//vue lifecycle
41+
console.log(this.ref, this.getter, this.prop, this.supProp, this.supGetter);
1742
}
1843
}
1944
```
@@ -23,18 +48,31 @@ is equal to
2348
```typescript
2449
import { defineComponent} from "vue";
2550
export default defineComponent({
26-
data(){
27-
return {
28-
prop:'p'
29-
}
51+
data() {
52+
return {
53+
supProp: "supProp",
54+
prop: "prop",
55+
};
56+
},
57+
methods: {
58+
supMethod() {},
59+
method() {
60+
this.$forceUpdate();
61+
},
62+
},
63+
computed: {
64+
supGetter() {
65+
return "supGetter";
66+
},
67+
getter() {
68+
return "getter";
3069
},
31-
methods:{
32-
meth(){
33-
this.$forceUpdate()
34-
}
70+
ref() {
71+
this.$refs["ref"];
3572
},
36-
mounted() {
37-
console.log('foo')
38-
}
39-
})
73+
},
74+
mounted() {
75+
console.log(this.ref, this.getter, this.prop, this.supProp, this.supGetter);
76+
},
77+
});
4078
```

src/component.ts

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,30 @@
11
import { defineComponent } from 'vue';
2-
import { obtainSlot } from './utils';
3-
4-
const LifecycleNames = [
5-
"beforeCreate",
6-
"created",
7-
"beforeMount",
8-
"mounted",
9-
"beforeUpdate",
10-
"updated",
11-
"activated",
12-
"deactivated",
13-
"beforeDestroy",
14-
"beforeUnmount",
15-
"destroyed",
16-
"unmounted",
17-
"renderTracked",
18-
"renderTriggered",
19-
"errorCaptured"
20-
]
21-
22-
23-
function makeObject(names: string[], obj: any) {
24-
return names.reduce<Record<string, any>>((pv, cv) => {
25-
pv[cv] = obj[cv]
26-
return pv
27-
}, {})
2+
import { build as optionComputed } from './option/computed'
3+
import { build as optionData } from './option/data'
4+
import { build as optionMethodsAndLifecycle } from './option/methodsAndLifecycle'
5+
import { build as optionRef } from './option/ref'
6+
export interface OptionBuilder {
7+
data?: Record<string, any>
8+
methods?: Record<string, Function>
9+
lifecycle?: Record<string, Function>
10+
computed?: Record<string, any>
2811
}
29-
export function Component(cons: { new(): any, prototype: any }) {
30-
const slot = obtainSlot(cons.prototype)
31-
const sample = new cons
32-
const proto = cons.prototype
33-
const LifecycleFunctions:Record<string,Function>={}
34-
const methodNames = Object.getOwnPropertyNames(proto).filter(name => {
35-
if (name === 'constructor') {
36-
return false
37-
}
38-
if (typeof proto[name] === 'function') {
39-
if(LifecycleNames.includes(name)){
40-
LifecycleFunctions[name]=proto[name]
41-
return false
42-
}
43-
44-
return true
45-
}
46-
})
47-
48-
const dataNames = Object.getOwnPropertyNames(sample)
49-
50-
const def = defineComponent({
12+
export interface Cons { new(): any, prototype: any }
13+
export function Component(cons: Cons) {
14+
const optionBuilder: OptionBuilder = {}
15+
optionComputed(cons,optionBuilder)
16+
optionMethodsAndLifecycle(cons, optionBuilder)
17+
optionRef(cons, optionBuilder)
18+
const raw = {
5119
data() {
52-
return makeObject(dataNames, sample)
20+
const optionBuilder: OptionBuilder = {}
21+
optionData(cons, optionBuilder)
22+
return optionBuilder.data ?? {}
5323
},
54-
methods: makeObject(methodNames, proto),
55-
...LifecycleFunctions
56-
})
24+
methods: optionBuilder.methods,
25+
computed: optionBuilder.computed,
26+
...optionBuilder.lifecycle
27+
}
28+
const def = defineComponent(raw)
5729
return def as any
58-
59-
6030
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
export { Component } from './component'
3+
export { decorator as Ref } from './option/ref'
34
import {
45
ComponentInternalInstance,
56
ComponentPublicInstance,
@@ -50,7 +51,7 @@ export class Base {
5051
beforeCreate?(): void;
5152
created?(): void;
5253
beforeMount?(): void;
53-
mounted?():void;
54+
mounted?(): void;
5455
beforeUpdate?(): void;
5556
updated?(): void;
5657
activated?(): void;

src/option/computed.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { makeObject } from '../utils'
2+
import { Cons, OptionBuilder } from '../component'
3+
import { obtainSlot, excludeNames, toBaseReverse, getValidNames } from '../utils'
4+
export function build(cons: Cons, optionBuilder: OptionBuilder) {
5+
optionBuilder.computed ??= {}
6+
const slot = obtainSlot(cons.prototype)
7+
const set: Set<string> = new Set
8+
slot.names.set('computed', set)
9+
const protoArr = toBaseReverse(cons.prototype)
10+
protoArr.forEach(proto => {
11+
getValidNames(proto, (des) => {
12+
return typeof des.get === 'function'
13+
}).forEach(name => {
14+
set.add(name)
15+
optionBuilder.computed![name] = Object.getOwnPropertyDescriptor(proto, name)!.get!
16+
})
17+
})
18+
}

src/option/data.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { makeObject } from '../utils'
2+
import { Cons, OptionBuilder } from '../component'
3+
import { obtainSlot, excludeNames, getValidNames } from '../utils'
4+
export function build(cons: Cons, optionBuilder: OptionBuilder) {
5+
optionBuilder.data ??= {}
6+
const sample = new cons
7+
8+
let names = getValidNames(sample, (des) => {
9+
return !!des.enumerable
10+
})
11+
12+
const slot = obtainSlot(cons.prototype)
13+
names = excludeNames(names, slot)
14+
Object.assign(optionBuilder.data, makeObject(names, sample))
15+
}

src/option/methodsAndLifecycle.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { makeObject, obtainSlot } from '../utils'
2+
import { Cons, OptionBuilder } from '../component'
3+
import { toBaseReverse, excludeNames, getValidNames } from '../utils'
4+
const LifecycleNames = [
5+
"beforeCreate",
6+
"created",
7+
"beforeMount",
8+
"mounted",
9+
"beforeUpdate",
10+
"updated",
11+
"activated",
12+
"deactivated",
13+
"beforeDestroy",
14+
"beforeUnmount",
15+
"destroyed",
16+
"unmounted",
17+
"renderTracked",
18+
"renderTriggered",
19+
"errorCaptured"
20+
]
21+
22+
export function build(cons: Cons, optionBuilder: OptionBuilder) {
23+
const slot = obtainSlot(cons.prototype)
24+
const protoArr = toBaseReverse(cons.prototype).reverse()
25+
optionBuilder.lifecycle ??= {}
26+
optionBuilder.methods ??= {}
27+
const LifecycleFunctions: Record<string, Function> = {}
28+
const MethodFunctions: Record<string, Function> = {}
29+
protoArr.forEach(proto => {
30+
excludeNames(getValidNames(proto, (des, name) => {
31+
if (name === 'constructor') {
32+
return false
33+
}
34+
if (typeof des.value === 'function') {
35+
return true
36+
}
37+
return false
38+
}), slot).forEach(name => {
39+
if (LifecycleNames.includes(name)) {
40+
LifecycleFunctions[name] = proto[name]
41+
}
42+
else {
43+
MethodFunctions[name] = proto[name]
44+
}
45+
})
46+
47+
48+
})
49+
50+
Object.assign(optionBuilder.methods, MethodFunctions)
51+
Object.assign(optionBuilder.lifecycle, LifecycleFunctions)
52+
53+
}

src/option/ref.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Cons, OptionBuilder } from '../component'
2+
import { obtainSlot } from '../utils'
3+
export function decorator(cons: any, name: string) {
4+
const slot = obtainSlot(cons)
5+
const set: Set<string> = new Set
6+
slot.names.set('ref', set)
7+
set.add(name)
8+
}
9+
10+
export function build(cons: Cons, optionBuilder: OptionBuilder) {
11+
optionBuilder.computed ??= {}
12+
const slot = obtainSlot(cons.prototype)
13+
const names = slot.names.get('ref')!
14+
names.forEach(name => {
15+
optionBuilder.computed![name] = function (this: any) {
16+
return this.$refs[name]
17+
}
18+
})
19+
20+
}

src/utils.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1+
import { Base } from './index'
12
const SlotSymbol = Symbol('vue-facing-decorator-slot')
23
class Slot {
3-
4+
names: Map<string, Set<string>> = new Map
45
}
56

67
export function makeSlot(obj: any): Slot {
78
if (obj[SlotSymbol]) {
89
throw ''
910
}
10-
return obj[SlotSymbol] = new Slot
11+
const slot = new Slot
12+
Object.defineProperty(obj, SlotSymbol, {
13+
enumerable: false,
14+
value: slot
15+
})
16+
return slot
1117
}
1218
export function getSlot(obj: any): Slot | undefined {
1319
return obj[SlotSymbol]
@@ -21,4 +27,37 @@ export function obtainSlot(obj: any): Slot {
2127
return makeSlot(obj)
2228

2329

30+
}
31+
32+
export function makeObject(names: string[], obj: any) {
33+
return names.reduce<Record<string, any>>((pv, cv) => {
34+
pv[cv] = obj[cv]
35+
return pv
36+
}, {})
37+
}
38+
39+
export function toBaseReverse(obj: any) {
40+
const arr: any[] = []
41+
let curr = obj
42+
while (curr.constructor !== Base) {
43+
arr.push(curr)
44+
curr = Object.getPrototypeOf(curr)
45+
}
46+
return arr
47+
}
48+
49+
export function excludeNames(names: string[], slot: Slot) {
50+
return names.filter(name => {
51+
for (const set of slot.names.values()) {
52+
if (set.has(name)) {
53+
return false
54+
}
55+
}
56+
return true
57+
})
58+
}
59+
60+
export function getValidNames(obj: any, filter: (des: PropertyDescriptor,name:string) => boolean) {
61+
const descriptors = Object.getOwnPropertyDescriptors(obj)
62+
return Object.keys(descriptors).filter(name => filter(descriptors[name],name))
2463
}

0 commit comments

Comments
 (0)