]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix resolving assets from mixins and extends
authorEvan You <yyx990803@gmail.com>
Wed, 26 Aug 2020 22:09:54 +0000 (18:09 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 26 Aug 2020 22:09:54 +0000 (18:09 -0400)
fix #1963

packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/helpers/resolveAssets.ts

index 5eee77f4d2d066ed404d5d6ee74f6b2438484538..5d5e895ca531d4dafc901d6b320cff35f45ca3e6 100644 (file)
@@ -151,4 +151,62 @@ describe('resolveAssets', () => {
       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)
+  })
 })
index c06b691b7bd750c5e82cf20f147fb5ffc712d4f2..0bc928911e5b0224f96d86b84e569c1fdeda483e 100644 (file)
@@ -24,7 +24,7 @@ import { Slots, initSlots, InternalSlots } from './componentSlots'
 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,
@@ -221,6 +221,17 @@ export interface ComponentInternalInstance {
    */
   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 ---------------------------------
 
   /**
@@ -372,6 +383,10 @@ export function createComponentInstance(
     accessCache: null!,
     renderCache: [],
 
+    // local resovled assets
+    components: null,
+    directives: null,
+
     // state
     ctx: EMPTY_OBJ,
     data: EMPTY_OBJ,
@@ -733,7 +748,8 @@ export function formatComponentName(
     }
     name =
       inferFromRegistry(
-        (instance.parent.type as ComponentOptions).components
+        instance.components ||
+          (instance.parent.type as ComponentOptions).components
       ) || inferFromRegistry(instance.appContext.components)
   }
 
index facca201d9e39ea338c3d58045cd21efec03dc68..d2a4c05944f49dd2e65a5447c92e72eca7d33e6b 100644 (file)
@@ -384,6 +384,9 @@ export function applyOptions(
     watch: watchOptions,
     provide: provideOptions,
     inject: injectOptions,
+    // assets
+    components,
+    directives,
     // lifecycle
     beforeMount,
     mounted,
@@ -568,6 +571,32 @@ export function applyOptions(
     }
   }
 
+  // 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)
index 4a20c747411dbe8c381801544b36ddec5a2ac141..59c9f4cc14cd930d92f7175268538db45746d764 100644 (file)
@@ -83,7 +83,8 @@ function resolveAsset(
 
     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) {