expect(serializeInner(root)).toBe('<div>hello</div>')
})
})
+
+ test('resolving from mixins & extends', () => {
+ const FooBar = () => null
+ const BarBaz = { mounted: () => null }
+
+ let component1: Component | string
+ let component2: Component | string
+ let component3: Component | string
+ let component4: Component | string
+ let directive1: Directive
+ let directive2: Directive
+ let directive3: Directive
+ let directive4: Directive
+
+ const Base = {
+ components: {
+ FooBar: FooBar
+ }
+ }
+ const Mixin = {
+ directives: {
+ BarBaz: BarBaz
+ }
+ }
+
+ const Root = {
+ extends: Base,
+ mixins: [Mixin],
+ setup() {
+ return () => {
+ component1 = resolveComponent('FooBar')!
+ directive1 = resolveDirective('BarBaz')!
+ // camelize
+ component2 = resolveComponent('Foo-bar')!
+ directive2 = resolveDirective('Bar-baz')!
+ // capitalize
+ component3 = resolveComponent('fooBar')!
+ directive3 = resolveDirective('barBaz')!
+ // camelize and capitalize
+ component4 = resolveComponent('foo-bar')!
+ directive4 = resolveDirective('bar-baz')!
+ }
+ }
+ }
+
+ const app = createApp(Root)
+ const root = nodeOps.createElement('div')
+ app.mount(root)
+ expect(component1!).toBe(FooBar)
+ expect(component2!).toBe(FooBar)
+ expect(component3!).toBe(FooBar)
+ expect(component4!).toBe(FooBar)
+
+ expect(directive1!).toBe(BarBaz)
+ expect(directive2!).toBe(BarBaz)
+ expect(directive3!).toBe(BarBaz)
+ expect(directive4!).toBe(BarBaz)
+ })
})
import { warn } from './warning'
import { ErrorCodes, callWithErrorHandling } from './errorHandling'
import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
-import { validateDirectiveName } from './directives'
+import { Directive, validateDirectiveName } from './directives'
import { applyOptions, ComponentOptions } from './componentOptions'
import {
EmitsOptions,
*/
renderCache: (Function | VNode)[]
+ /**
+ * Resolved component registry, only for components with mixins or extends
+ * @internal
+ */
+ components: Record<string, ConcreteComponent> | null
+ /**
+ * Resolved directive registry, only for components with mixins or extends
+ * @internal
+ */
+ directives: Record<string, Directive> | null
+
// the rest are only for stateful components ---------------------------------
/**
accessCache: null!,
renderCache: [],
+ // local resovled assets
+ components: null,
+ directives: null,
+
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
}
name =
inferFromRegistry(
- (instance.parent.type as ComponentOptions).components
+ instance.components ||
+ (instance.parent.type as ComponentOptions).components
) || inferFromRegistry(instance.appContext.components)
}
watch: watchOptions,
provide: provideOptions,
inject: injectOptions,
+ // assets
+ components,
+ directives,
// lifecycle
beforeMount,
mounted,
}
}
+ // asset options.
+ // To reduce memory usage, only components with mixins or extends will have
+ // resolved asset registry attached to instance.
+ if (asMixin) {
+ if (components) {
+ extend(
+ instance.components ||
+ (instance.components = extend(
+ {},
+ (instance.type as ComponentOptions).components
+ ) as Record<string, ConcreteComponent>),
+ components
+ )
+ }
+ if (directives) {
+ extend(
+ instance.directives ||
+ (instance.directives = extend(
+ {},
+ (instance.type as ComponentOptions).directives
+ )),
+ directives
+ )
+ }
+ }
+
// lifecycle options
if (!asMixin) {
callSyncHook('created', options, publicThis, globalMixins)
const res =
// local registration
- resolve((Component as ComponentOptions)[type], name) ||
+ // check instance[type] first for components with mixin or extends.
+ resolve(instance[type] || (Component as ComponentOptions)[type], name) ||
// global registration
resolve(instance.appContext[type], name)
if (__DEV__ && warnMissing && !res) {