diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index 85800fef..43ba48b9 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -798,9 +798,18 @@ describe('compiler: element transform', () => {
describe('dynamic component', () => {
test('static binding', () => {
const { node, root } = parseWithBind(``)
- expect(root.helpers).not.toContain(RESOLVE_DYNAMIC_COMPONENT)
+ expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(node).toMatchObject({
- tag: '_component_foo'
+ tag: {
+ callee: RESOLVE_DYNAMIC_COMPONENT,
+ arguments: [
+ {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: 'foo',
+ isStatic: true
+ }
+ ]
+ }
})
})
@@ -813,7 +822,8 @@ describe('compiler: element transform', () => {
arguments: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
- content: 'foo'
+ content: 'foo',
+ isStatic: false
}
]
}
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index 3123d8ec..155c1dcf 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -204,19 +204,13 @@ export function resolveComponentType(
// 1. dynamic component
const isProp = node.tag === 'component' && findProp(node, 'is')
if (isProp) {
- // static
- if (isProp.type === NodeTypes.ATTRIBUTE) {
- const isType = isProp.value && isProp.value.content
- if (isType) {
- context.helper(RESOLVE_COMPONENT)
- context.components.add(isType)
- return toValidAssetId(isType, `component`)
- }
- }
- // dynamic
- else if (isProp.exp) {
+ const exp =
+ isProp.type === NodeTypes.ATTRIBUTE
+ ? isProp.value && createSimpleExpression(isProp.value.content, true)
+ : isProp.exp
+ if (exp) {
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
- isProp.exp
+ exp
])
}
}
diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
index 8abae35d..c0275dc9 100644
--- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
+++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts
@@ -20,13 +20,11 @@ describe('ssr: components', () => {
test('dynamic component', () => {
expect(compile(``).code)
.toMatchInlineSnapshot(`
- "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
+ "const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
- const _component_foo = _resolveComponent(\\"foo\\")
-
- _push(_ssrRenderComponent(_component_foo, { prop: \\"b\\" }, null, _parent))
+ _push(_ssrRenderComponent(_resolveDynamicComponent(\\"foo\\"), { prop: \\"b\\" }, null, _parent))
}"
`)
diff --git a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
index 6cac9b24..0e56ba9f 100644
--- a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
+++ b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
@@ -6,11 +6,14 @@ import {
Component,
Directive,
resolveDynamicComponent,
- h
+ h,
+ serializeInner
} from '@vue/runtime-test'
import { mockWarn } from '@vue/shared'
describe('resolveAssets', () => {
+ mockWarn()
+
test('should work', () => {
const FooBar = () => null
const BarBaz = { mounted: () => null }
@@ -63,8 +66,6 @@ describe('resolveAssets', () => {
})
describe('warning', () => {
- mockWarn()
-
test('used outside render() or setup()', () => {
resolveComponent('foo')
expect(
@@ -128,5 +129,22 @@ describe('resolveAssets', () => {
expect(bar).toBe(dynamicComponents.bar)
expect(baz).toBe(dynamicComponents.baz)
})
+
+ test('resolve dynamic component should fallback to plain element without warning', () => {
+ const Root = {
+ setup() {
+ return () => {
+ return h(resolveDynamicComponent('div') as string, null, {
+ default: () => 'hello'
+ })
+ }
+ }
+ }
+
+ const app = createApp(Root)
+ const root = nodeOps.createElement('div')
+ app.mount(root)
+ expect(serializeInner(root)).toBe('
hello
')
+ })
})
})
diff --git a/packages/runtime-core/__tests__/vnode.spec.ts b/packages/runtime-core/__tests__/vnode.spec.ts
index bb84aa49..7c11c2ab 100644
--- a/packages/runtime-core/__tests__/vnode.spec.ts
+++ b/packages/runtime-core/__tests__/vnode.spec.ts
@@ -106,7 +106,7 @@ describe('vnode', () => {
const vnode = createVNode('p', null, ['foo'])
expect(vnode.children).toMatchObject(['foo'])
expect(vnode.shapeFlag).toBe(
- ShapeFlags.ELEMENT + ShapeFlags.ARRAY_CHILDREN
+ ShapeFlags.ELEMENT | ShapeFlags.ARRAY_CHILDREN
)
})
@@ -114,7 +114,7 @@ describe('vnode', () => {
const vnode = createVNode('p', null, { foo: 'foo' })
expect(vnode.children).toMatchObject({ foo: 'foo' })
expect(vnode.shapeFlag).toBe(
- ShapeFlags.ELEMENT + ShapeFlags.SLOTS_CHILDREN
+ ShapeFlags.ELEMENT | ShapeFlags.SLOTS_CHILDREN
)
})
@@ -122,7 +122,7 @@ describe('vnode', () => {
const vnode = createVNode('p', null, nop)
expect(vnode.children).toMatchObject({ default: nop })
expect(vnode.shapeFlag).toBe(
- ShapeFlags.ELEMENT + ShapeFlags.SLOTS_CHILDREN
+ ShapeFlags.ELEMENT | ShapeFlags.SLOTS_CHILDREN
)
})
@@ -130,7 +130,19 @@ describe('vnode', () => {
const vnode = createVNode('p', null, 'foo')
expect(vnode.children).toBe('foo')
expect(vnode.shapeFlag).toBe(
- ShapeFlags.ELEMENT + ShapeFlags.TEXT_CHILDREN
+ ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN
+ )
+ })
+
+ test('element with slots', () => {
+ const children = [createVNode('span', null, 'hello')]
+ const vnode = createVNode('div', null, {
+ default: () => children
+ })
+
+ expect(vnode.children).toBe(children)
+ expect(vnode.shapeFlag).toBe(
+ ShapeFlags.ELEMENT | ShapeFlags.ARRAY_CHILDREN
)
})
})
diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts
index 9c3dee57..f0c57559 100644
--- a/packages/runtime-core/src/helpers/resolveAssets.ts
+++ b/packages/runtime-core/src/helpers/resolveAssets.ts
@@ -24,10 +24,14 @@ export function resolveComponent(name: string): Component | undefined {
export function resolveDynamicComponent(
component: unknown
-): Component | undefined {
+): Component | string | undefined {
if (!component) return
if (isString(component)) {
- return resolveAsset(COMPONENTS, component, currentRenderingInstance)
+ return (
+ resolveAsset(COMPONENTS, component, currentRenderingInstance, false) ||
+ // fallback to plain element
+ component
+ )
} else if (isFunction(component) || isObject(component)) {
return component
}
@@ -41,7 +45,8 @@ export function resolveDirective(name: string): Directive | undefined {
function resolveAsset(
type: typeof COMPONENTS,
name: string,
- instance?: ComponentInternalInstance | null
+ instance?: ComponentInternalInstance | null,
+ warnMissing?: boolean
): Component | undefined
// overload 2: directives
function resolveAsset(
@@ -54,7 +59,8 @@ function resolveAsset(
type: typeof COMPONENTS | typeof DIRECTIVES,
name: string,
instance: ComponentInternalInstance | null = currentRenderingInstance ||
- currentInstance
+ currentInstance,
+ warnMissing = true
) {
if (instance) {
let camelized, capitalized
@@ -75,7 +81,8 @@ function resolveAsset(
res = self
}
}
- if (__DEV__ && !res) {
+ if (__DEV__ && warnMissing && !res) {
+ debugger
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
}
return res
diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts
index 29b1fb0d..78599e63 100644
--- a/packages/runtime-core/src/vnode.ts
+++ b/packages/runtime-core/src/vnode.ts
@@ -397,9 +397,16 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
} else if (isArray(children)) {
type = ShapeFlags.ARRAY_CHILDREN
} else if (typeof children === 'object') {
- type = ShapeFlags.SLOTS_CHILDREN
- if (!(children as RawSlots)._) {
- ;(children as RawSlots)._ctx = currentRenderingInstance
+ // in case resolves to native element, the vnode call
+ // will receive slots object.
+ if (vnode.shapeFlag & ShapeFlags.ELEMENT && (children as any).default) {
+ normalizeChildren(vnode, (children as any).default())
+ return
+ } else {
+ type = ShapeFlags.SLOTS_CHILDREN
+ if (!(children as RawSlots)._) {
+ ;(children as RawSlots)._ctx = currentRenderingInstance
+ }
}
} else if (isFunction(children)) {
children = { default: children, _ctx: currentRenderingInstance }