parent
1b2149dbb2
commit
f529dbde23
@ -798,9 +798,18 @@ describe('compiler: element transform', () => {
|
|||||||
describe('dynamic component', () => {
|
describe('dynamic component', () => {
|
||||||
test('static binding', () => {
|
test('static binding', () => {
|
||||||
const { node, root } = parseWithBind(`<component is="foo" />`)
|
const { node, root } = parseWithBind(`<component is="foo" />`)
|
||||||
expect(root.helpers).not.toContain(RESOLVE_DYNAMIC_COMPONENT)
|
expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
|
||||||
expect(node).toMatchObject({
|
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: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
content: 'foo'
|
content: 'foo',
|
||||||
|
isStatic: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -204,19 +204,13 @@ export function resolveComponentType(
|
|||||||
// 1. dynamic component
|
// 1. dynamic component
|
||||||
const isProp = node.tag === 'component' && findProp(node, 'is')
|
const isProp = node.tag === 'component' && findProp(node, 'is')
|
||||||
if (isProp) {
|
if (isProp) {
|
||||||
// static <component is="foo" />
|
const exp =
|
||||||
if (isProp.type === NodeTypes.ATTRIBUTE) {
|
isProp.type === NodeTypes.ATTRIBUTE
|
||||||
const isType = isProp.value && isProp.value.content
|
? isProp.value && createSimpleExpression(isProp.value.content, true)
|
||||||
if (isType) {
|
: isProp.exp
|
||||||
context.helper(RESOLVE_COMPONENT)
|
if (exp) {
|
||||||
context.components.add(isType)
|
|
||||||
return toValidAssetId(isType, `component`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// dynamic <component :is="asdf" />
|
|
||||||
else if (isProp.exp) {
|
|
||||||
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
|
return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [
|
||||||
isProp.exp
|
exp
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,11 @@ describe('ssr: components', () => {
|
|||||||
test('dynamic component', () => {
|
test('dynamic component', () => {
|
||||||
expect(compile(`<component is="foo" prop="b" />`).code)
|
expect(compile(`<component is="foo" prop="b" />`).code)
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
|
"const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx } = require(\\"vue\\")
|
||||||
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
|
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
|
||||||
|
|
||||||
return function ssrRender(_ctx, _push, _parent) {
|
return function ssrRender(_ctx, _push, _parent) {
|
||||||
const _component_foo = _resolveComponent(\\"foo\\")
|
_push(_ssrRenderComponent(_resolveDynamicComponent(\\"foo\\"), { prop: \\"b\\" }, null, _parent))
|
||||||
|
|
||||||
_push(_ssrRenderComponent(_component_foo, { prop: \\"b\\" }, null, _parent))
|
|
||||||
}"
|
}"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
@ -6,11 +6,14 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
Directive,
|
Directive,
|
||||||
resolveDynamicComponent,
|
resolveDynamicComponent,
|
||||||
h
|
h,
|
||||||
|
serializeInner
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { mockWarn } from '@vue/shared'
|
import { mockWarn } from '@vue/shared'
|
||||||
|
|
||||||
describe('resolveAssets', () => {
|
describe('resolveAssets', () => {
|
||||||
|
mockWarn()
|
||||||
|
|
||||||
test('should work', () => {
|
test('should work', () => {
|
||||||
const FooBar = () => null
|
const FooBar = () => null
|
||||||
const BarBaz = { mounted: () => null }
|
const BarBaz = { mounted: () => null }
|
||||||
@ -63,8 +66,6 @@ describe('resolveAssets', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('warning', () => {
|
describe('warning', () => {
|
||||||
mockWarn()
|
|
||||||
|
|
||||||
test('used outside render() or setup()', () => {
|
test('used outside render() or setup()', () => {
|
||||||
resolveComponent('foo')
|
resolveComponent('foo')
|
||||||
expect(
|
expect(
|
||||||
@ -128,5 +129,22 @@ describe('resolveAssets', () => {
|
|||||||
expect(bar).toBe(dynamicComponents.bar)
|
expect(bar).toBe(dynamicComponents.bar)
|
||||||
expect(baz).toBe(dynamicComponents.baz)
|
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('<div>hello</div>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -106,7 +106,7 @@ describe('vnode', () => {
|
|||||||
const vnode = createVNode('p', null, ['foo'])
|
const vnode = createVNode('p', null, ['foo'])
|
||||||
expect(vnode.children).toMatchObject(['foo'])
|
expect(vnode.children).toMatchObject(['foo'])
|
||||||
expect(vnode.shapeFlag).toBe(
|
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' })
|
const vnode = createVNode('p', null, { foo: 'foo' })
|
||||||
expect(vnode.children).toMatchObject({ foo: 'foo' })
|
expect(vnode.children).toMatchObject({ foo: 'foo' })
|
||||||
expect(vnode.shapeFlag).toBe(
|
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)
|
const vnode = createVNode('p', null, nop)
|
||||||
expect(vnode.children).toMatchObject({ default: nop })
|
expect(vnode.children).toMatchObject({ default: nop })
|
||||||
expect(vnode.shapeFlag).toBe(
|
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')
|
const vnode = createVNode('p', null, 'foo')
|
||||||
expect(vnode.children).toBe('foo')
|
expect(vnode.children).toBe('foo')
|
||||||
expect(vnode.shapeFlag).toBe(
|
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
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -24,10 +24,14 @@ export function resolveComponent(name: string): Component | undefined {
|
|||||||
|
|
||||||
export function resolveDynamicComponent(
|
export function resolveDynamicComponent(
|
||||||
component: unknown
|
component: unknown
|
||||||
): Component | undefined {
|
): Component | string | undefined {
|
||||||
if (!component) return
|
if (!component) return
|
||||||
if (isString(component)) {
|
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)) {
|
} else if (isFunction(component) || isObject(component)) {
|
||||||
return component
|
return component
|
||||||
}
|
}
|
||||||
@ -41,7 +45,8 @@ export function resolveDirective(name: string): Directive | undefined {
|
|||||||
function resolveAsset(
|
function resolveAsset(
|
||||||
type: typeof COMPONENTS,
|
type: typeof COMPONENTS,
|
||||||
name: string,
|
name: string,
|
||||||
instance?: ComponentInternalInstance | null
|
instance?: ComponentInternalInstance | null,
|
||||||
|
warnMissing?: boolean
|
||||||
): Component | undefined
|
): Component | undefined
|
||||||
// overload 2: directives
|
// overload 2: directives
|
||||||
function resolveAsset(
|
function resolveAsset(
|
||||||
@ -54,7 +59,8 @@ function resolveAsset(
|
|||||||
type: typeof COMPONENTS | typeof DIRECTIVES,
|
type: typeof COMPONENTS | typeof DIRECTIVES,
|
||||||
name: string,
|
name: string,
|
||||||
instance: ComponentInternalInstance | null = currentRenderingInstance ||
|
instance: ComponentInternalInstance | null = currentRenderingInstance ||
|
||||||
currentInstance
|
currentInstance,
|
||||||
|
warnMissing = true
|
||||||
) {
|
) {
|
||||||
if (instance) {
|
if (instance) {
|
||||||
let camelized, capitalized
|
let camelized, capitalized
|
||||||
@ -75,7 +81,8 @@ function resolveAsset(
|
|||||||
res = self
|
res = self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (__DEV__ && !res) {
|
if (__DEV__ && warnMissing && !res) {
|
||||||
|
debugger
|
||||||
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
|
warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
|
@ -397,9 +397,16 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
|
|||||||
} else if (isArray(children)) {
|
} else if (isArray(children)) {
|
||||||
type = ShapeFlags.ARRAY_CHILDREN
|
type = ShapeFlags.ARRAY_CHILDREN
|
||||||
} else if (typeof children === 'object') {
|
} else if (typeof children === 'object') {
|
||||||
type = ShapeFlags.SLOTS_CHILDREN
|
// in case <component :is="x"> resolves to native element, the vnode call
|
||||||
if (!(children as RawSlots)._) {
|
// will receive slots object.
|
||||||
;(children as RawSlots)._ctx = currentRenderingInstance
|
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)) {
|
} else if (isFunction(children)) {
|
||||||
children = { default: children, _ctx: currentRenderingInstance }
|
children = { default: children, _ctx: currentRenderingInstance }
|
||||||
|
Loading…
Reference in New Issue
Block a user