Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9d11761

Browse filesBrowse files
committed
fix: avoid extended mixin missing own options (fix #467)
1 parent 22dce0b commit 9d11761
Copy full SHA for 9d11761

File tree

Expand file treeCollapse file tree

3 files changed

+107
-53
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+107
-53
lines changed

‎src/helpers.ts

Copy file name to clipboardExpand all lines: src/helpers.ts
+6-6Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function Options<V extends Vue>(
66
options: ComponentOptions & ThisType<V>
77
): <VC extends VueConstructor<VueBase>>(target: VC) => VC {
88
return (Component) => {
9-
Component.__vccBase = options
9+
Component.__o = options
1010
return Component
1111
}
1212
}
@@ -34,13 +34,13 @@ export function createDecorator(
3434
typeof target === 'function'
3535
? target
3636
: (target.constructor as VueConstructor)
37-
if (!Ctor.__vccDecorators) {
38-
Ctor.__vccDecorators = []
37+
if (!Ctor.__d) {
38+
Ctor.__d = []
3939
}
4040
if (typeof index !== 'number') {
4141
index = undefined
4242
}
43-
Ctor.__vccDecorators.push((options) => factory(options, key, index))
43+
Ctor.__d.push((options) => factory(options, key, index))
4444
}
4545
}
4646

@@ -59,8 +59,8 @@ export type MixedVueBase<Mixins extends VueMixin[]> = Mixins extends (infer T)[]
5959
export function mixins<T extends VueMixin[]>(...Ctors: T): MixedVueBase<T>
6060
export function mixins(...Ctors: VueMixin[]): VueConstructor {
6161
return class MixedVue extends Vue {
62-
static __vccExtend(options: ComponentOptions) {
63-
Ctors.forEach((Ctor) => Ctor.__vccExtend(options))
62+
static __b: ComponentOptions = {
63+
mixins: Ctors.map((Ctor) => Ctor.__vccOpts),
6464
}
6565

6666
constructor(...args: any[]) {

‎src/vue.ts

Copy file name to clipboardExpand all lines: src/vue.ts
+62-47Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,52 @@ function getSuper(Ctor: typeof VueImpl): typeof VueImpl | undefined {
4646
return superProto.constructor as typeof VueImpl
4747
}
4848

49+
function getOwn<T extends Object, K extends keyof T>(
50+
value: T,
51+
key: K
52+
): T[K] | undefined {
53+
return value.hasOwnProperty(key) ? value[key] : undefined
54+
}
55+
4956
export interface VueStatic {
5057
// -- Class component configs
5158

52-
/** @internal */
53-
__vccCache?: ComponentOptions
54-
55-
/** @internal */
56-
__vccBase?: ComponentOptions
57-
58-
/** @internal */
59-
__vccDecorators?: ((options: ComponentOptions) => void)[]
60-
61-
/** @internal */
62-
__vccExtend: (options: ComponentOptions) => void
63-
64-
/** @internal */
65-
__vccHooks: string[]
66-
67-
/** @internal */
59+
/**
60+
* @internal
61+
* The cache of __vccOpts
62+
*/
63+
__c?: ComponentOptions
64+
65+
/**
66+
* @internal
67+
* The base options specified to this class.
68+
*/
69+
__b?: ComponentOptions
70+
71+
/**
72+
* @internal
73+
* Component options specified with `@Options` decorator
74+
*/
75+
__o?: ComponentOptions
76+
77+
/**
78+
* @internal
79+
* Decorators applied to this class.
80+
*/
81+
__d?: ((options: ComponentOptions) => void)[]
82+
83+
/**
84+
* @internal
85+
* Registered (lifecycle) hooks that will be ported from class methods
86+
* into component options.
87+
*/
88+
__h: string[]
89+
90+
/**
91+
* @internal
92+
* Final component options object that Vue core processes.
93+
* The name must be __vccOpts since it is the contract with the Vue core.
94+
*/
6895
__vccOpts: ComponentOptions
6996

7097
// --- Vue Loader etc injections
@@ -147,17 +174,7 @@ export interface VueConstructor<V extends VueBase = Vue> extends VueMixin<V> {
147174
}
148175

149176
class VueImpl {
150-
/** @internal */
151-
static __vccCache?: ComponentOptions
152-
153-
/** @internal */
154-
static __vccBase?: ComponentOptions
155-
156-
/** @internal */
157-
static __vccDecorators?: ((options: ComponentOptions) => void)[]
158-
159-
/** @internal */
160-
static __vccHooks = [
177+
static __h = [
161178
'data',
162179
'beforeCreate',
163180
'created',
@@ -174,35 +191,34 @@ class VueImpl {
174191
'serverPrefetch',
175192
]
176193

177-
/** @internal */
178-
static __vccExtend(options: ComponentOptions) {
179-
options.mixins = options.mixins || []
180-
options.mixins.push(this.__vccOpts)
181-
}
182-
183-
/** @internal */
184194
static get __vccOpts(): ComponentOptions {
185195
// Early return if `this` is base class as it does not have any options
186196
if (this === Vue) {
187197
return {}
188198
}
189199

190-
const cache = this.hasOwnProperty('__vccCache') && this.__vccCache
200+
const Ctor = this as VueConstructor
201+
202+
const cache = getOwn(Ctor, '__c')
191203
if (cache) {
192204
return cache
193205
}
194206

195-
const Ctor = this
196-
197207
// If the options are provided via decorator use it as a base
198-
const options = (this.__vccCache = this.hasOwnProperty('__vccBase')
199-
? { ...this.__vccBase }
200-
: {})
208+
const options: ComponentOptions = { ...getOwn(Ctor, '__o') }
209+
Ctor.__c = options
201210

202211
// Handle super class options
203212
const Super = getSuper(Ctor)
204213
if (Super) {
205-
Super.__vccExtend(options)
214+
options.extends = Super.__vccOpts
215+
}
216+
217+
// Inject base options as a mixin
218+
const base = getOwn(Ctor, '__b')
219+
if (base) {
220+
options.mixins = options.mixins || []
221+
options.mixins.unshift(base)
206222
}
207223

208224
options.methods = { ...options.methods }
@@ -215,7 +231,7 @@ class VueImpl {
215231
}
216232

217233
// hooks
218-
if (Ctor.__vccHooks.indexOf(key) > -1) {
234+
if (Ctor.__h.indexOf(key) > -1) {
219235
;(options as any)[key] = (proto as any)[key]
220236
return
221237
}
@@ -280,8 +296,7 @@ class VueImpl {
280296
return promise ?? plainData
281297
}
282298

283-
const decorators =
284-
this.hasOwnProperty('__vccDecorators') && this.__vccDecorators
299+
const decorators = getOwn(Ctor, '__d')
285300
if (decorators) {
286301
decorators.forEach((fn) => fn(options))
287302
}
@@ -305,7 +320,7 @@ class VueImpl {
305320
}
306321

307322
static registerHooks(keys: string[]): void {
308-
this.__vccHooks.push(...keys)
323+
this.__h.push(...keys)
309324
}
310325

311326
static props(Props: { new (): unknown }): VueConstructor {
@@ -318,8 +333,8 @@ class VueImpl {
318333
})
319334

320335
class PropsMixin extends this {
321-
static __vccExtend(options: ComponentOptions) {
322-
options.props = props
336+
static __b: ComponentOptions = {
337+
props,
323338
}
324339
}
325340
return PropsMixin as VueConstructor

‎test/specs/test.spec.ts

Copy file name to clipboardExpand all lines: test/specs/test.spec.ts
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,45 @@ describe('vue-class-component', () => {
348348
expect(root.valueB).toBe(456)
349349
})
350350

351+
it('nested mixins', () => {
352+
class GrandParentA extends Vue {
353+
get a() {
354+
return 'Hello'
355+
}
356+
}
357+
358+
class GrandParentB extends Vue {
359+
get b() {
360+
return 'World'
361+
}
362+
}
363+
364+
class ParentA extends mixins(GrandParentA, GrandParentB) {
365+
get helloWorld() {
366+
return this.a + this.b
367+
}
368+
}
369+
370+
class ParentB extends Vue {
371+
get c() {
372+
return 'Foobar'
373+
}
374+
}
375+
376+
class App extends mixins(ParentA, ParentB) {
377+
get d() {
378+
return 'Test'
379+
}
380+
}
381+
382+
const { root } = mount(App)
383+
expect(root.a).toBe('Hello')
384+
expect(root.b).toBe('World')
385+
expect(root.helloWorld).toBe('HelloWorld')
386+
expect(root.c).toBe('Foobar')
387+
expect(root.d).toBe('Test')
388+
})
389+
351390
it('props class', () => {
352391
class Props {
353392
foo?: string

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.