Skip to content

Commit efcc7f7

Browse files
committed
mixins function
1 parent eed3636 commit efcc7f7

File tree

11 files changed

+170
-27
lines changed

11 files changed

+170
-27
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Component, ComponentBase, Vue, mixins } from 'vue-facing-decorator'
2+
3+
/*
4+
Vue options API
5+
{
6+
name:"MyComponent",
7+
extends:{
8+
mixins:[{
9+
name:'ComponentA'
10+
},{
11+
name:'ComponentB'
12+
}]
13+
}
14+
}
15+
*/
16+
@ComponentBase({
17+
name: "ComponentA"
18+
})
19+
class ComponentA extends Vue {
20+
21+
}
22+
23+
@ComponentBase({
24+
name: "ComponentB"
25+
})
26+
class ComponentB extends Vue {
27+
28+
}
29+
30+
@Component({
31+
name: "MyComponent"
32+
})
33+
export default class MyComponent extends mixins(ComponentA,ComponentB) {
34+
35+
}

docs/en/inheritance/component/component.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ Consider code:
1010

1111
There are two components: `MyComponent` and `SuperComponent`. The inheritance is implemented by vue `extends`.
1212

13+
## Extends multiple components
14+
15+
Use `mixins` function to extends multiple components decorated by `ComponentBase`.
16+
17+
[](./code-mixins-function.ts ':include :type=code typescript')
18+
19+
1320
## For vue native components
1421

1522
Use `mixins` to merge vue native components.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
import { Component, ComponentBase, Vue, mixins } from 'vue-facing-decorator'
3+
4+
/*
5+
Vue options API
6+
{
7+
name:"MyComponent",
8+
extends:{
9+
mixins:[{
10+
name:'ComponentA'
11+
},{
12+
name:'ComponentB'
13+
}]
14+
}
15+
}
16+
*/
17+
@ComponentBase({
18+
name: "ComponentA"
19+
})
20+
class ComponentA extends Vue {
21+
22+
}
23+
24+
@ComponentBase({
25+
name: "ComponentB"
26+
})
27+
class ComponentB extends Vue {
28+
29+
}
30+
31+
@Component({
32+
name: "MyComponent"
33+
})
34+
export default class MyComponent extends mixins(ComponentA,ComponentB) {
35+
36+
}

docs/zh-cn/inheritance/component/component.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
这里有两个组件:`MyComponent``SuperComponent`。继承是通过vue `extends`实现的。
1212

13+
## 继承多组件
14+
15+
使用 `mixins` 函数来继承多个通过 `ComponentBase` 装饰的组件。
16+
17+
[](./code-mixins-function.ts ':include :type=code typescript')
18+
1319
## 关于vue原生组件
1420

1521
使用`mixins`来合并vue原生组件。

package-lock.json

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"build:esm": "./node_modules/.bin/tsc -outDir dist/esm -module ES6",
2424
"lint": "eslint .",
2525
"lint:fix": "eslint . --fix",
26-
"watch":"./node_modules/.bin/tsc -outDir dist/esm -module ES6 --watch"
26+
"watch": "./node_modules/.bin/tsc -outDir dist/esm -module ES6 --watch"
2727
},
2828
"author": "",
2929
"license": "MIT",
@@ -44,13 +44,13 @@
4444
"jsdom-global": "^3.0.2",
4545
"mocha": "^10.0.0",
4646
"ts-node": "^10.8.1",
47-
"typescript": "latest",
48-
"vue": "^3.2.37"
47+
"vue": "^3.2.37",
48+
"typescript": "latest"
4949
},
5050
"homepage": "https://github.com/facing-dev/vue-facing-decorator",
5151
"repository": {
5252
"type": "git",
5353
"url": "[email protected]:facing-dev/vue-facing-decorator.git"
54-
},
55-
"dependencies": {}
54+
}
55+
5656
}

src/component.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type Cons = VueCons
1818

1919
type SetupFunction<T> = (this: void, props: Readonly<any>, ctx: SetupContext<any>) => T | Promise<T>
2020
export type OptionSetupFunction = SetupFunction<any>
21-
export type ComponentSetupFunction = SetupFunction<Record<string,any>>
21+
export type ComponentSetupFunction = SetupFunction<Record<string, any>>
2222
function ComponentOption(cons: Cons, extend?: any) {
2323

2424
const optionBuilder: OptionBuilder = {}
@@ -74,7 +74,7 @@ function buildComponent(cons: Cons, arg: ComponentOption, extend?: any): any {
7474
const option = ComponentOption(cons, extend)
7575
const slot = obtainSlot(cons.prototype)
7676
Object.keys(arg).reduce<Record<string, any>>((option, name: string) => {
77-
if (['options', 'modifier', 'emits','setup'].includes(name)) {
77+
if (['options', 'modifier', 'emits', 'setup'].includes(name)) {
7878
return option
7979
}
8080
option[name] = arg[name as keyof ComponentOption]
@@ -91,17 +91,17 @@ function buildComponent(cons: Cons, arg: ComponentOption, extend?: any): any {
9191
} else {
9292
const oldSetup: OptionSetupFunction = option.setup
9393
const newSetup: ComponentSetupFunction = arg.setup
94-
95-
const setup: ComponentSetupFunction = function (props, ctx) {
94+
95+
const setup: ComponentSetupFunction = function (props, ctx) {
9696
const newRet = newSetup(props, ctx)
9797
const oldRet = oldSetup(props, ctx)
98-
if(oldRet instanceof Promise || newRet instanceof Promise){
99-
return Promise.all([newRet,oldRet]).then((arr)=>{
100-
return Object.assign({},arr[0],arr[1])
98+
if (oldRet instanceof Promise || newRet instanceof Promise) {
99+
return Promise.all([newRet, oldRet]).then((arr) => {
100+
return Object.assign({}, arr[0], arr[1])
101101
})
102-
}else{
102+
} else {
103103

104-
return Object.assign({},newRet,oldRet)
104+
return Object.assign({}, newRet, oldRet)
105105
}
106106

107107
}
@@ -151,12 +151,6 @@ export function ComponentBase(arg: ComponentConsOption): any {
151151
export function Component(arg: ComponentConsOption): any {
152152
return _Component(arg, function (cons: Cons, option: ComponentOption) {
153153
build(cons, option)
154-
// const slot = getSlot(cons.prototype)!
155-
// Object.defineProperty(cons, '__vccOpts', {
156-
// value: slot.cachedVueComponent
157-
// })
158-
// console.log('kkkk', '__vccOpts' in cons, cons)
159-
// return cons
160154
return obtainSlot(cons.prototype).cachedVueComponent
161155
})
162156
}

src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export { decorator as Emit } from './option/emit'
88
export { decorator as VModel, decorator as Model } from './option/vmodel'
99
export { decorator as Vanilla } from './option/vanilla'
1010
export { decorator as Hook } from './option/methodsAndHooks'
11+
export { mixins } from './mixins'
1112
import type { ComponentPublicInstance } from 'vue'
1213
import type { OptionBuilder } from './optionBuilder'
1314

@@ -24,8 +25,8 @@ export function TSX<Properties extends {} = {}, Events extends {} = {}>() {
2425
}
2526
}
2627

27-
export type VueCons = {
28-
new(optionBuilder: OptionBuilder, vueInstance: any): ComponentPublicInstance & BaseTypeIdentify
28+
export type VueCons<T = {}> = {
29+
new(optionBuilder: OptionBuilder, vueInstance: any): ComponentPublicInstance & BaseTypeIdentify & T
2930
}
3031

3132
export const Base = class {

src/mixins.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ComponentBase } from './component'
2+
import { obtainSlot } from './utils'
3+
import type { VueCons } from './index'
4+
import { Vue } from './index'
5+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
6+
type MixedClass<Mixins extends VueCons[]> = UnionToIntersection<{ [index in keyof Mixins]: InstanceType<Mixins[index]> }[number]>
7+
export function mixins<T extends VueCons[]>(...conses: T) {
8+
class MixinsClass extends Vue {
9+
10+
}
11+
ComponentBase({
12+
mixins: conses.map((cons => obtainSlot(cons.prototype).cachedVueComponent))
13+
})(MixinsClass)
14+
return MixinsClass as any as VueCons<MixedClass<T>>
15+
}
16+
17+
18+
19+
20+
21+
22+

test/feature/mixinsFunction.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
import { expect } from 'chai';
3+
import 'mocha';
4+
import { Component, ComponentBase, Vue, mixins } from '../../dist'
5+
6+
@ComponentBase
7+
class A extends Vue {
8+
methodA(){
9+
return 'methodAValue'
10+
}
11+
}
12+
13+
@ComponentBase
14+
class B extends Vue {
15+
16+
methodB(){
17+
return 'methodBValue'
18+
}
19+
}
20+
21+
@Component
22+
class Comp extends mixins(A,B){
23+
24+
}
25+
26+
const CompContext = Comp as any
27+
28+
29+
30+
describe('mixins function',
31+
() => {
32+
it('default', () => {
33+
expect('object').to.equal(typeof CompContext.extends)
34+
expect(true).to.equal(Array.isArray(CompContext.extends.mixins))
35+
expect('function').to.equal(typeof CompContext.extends.mixins[0].methods.methodA)
36+
expect('methodAValue').to.equal(CompContext.extends.mixins[0].methods.methodA())
37+
expect('function').to.equal(typeof CompContext.extends.mixins[0].methods.methodA)
38+
expect('methodBValue').to.equal(CompContext.extends.mixins[1].methods.methodB())
39+
})
40+
}
41+
)
42+
export default {}

0 commit comments

Comments
 (0)