`;
exports[`compiler: element transform > dynamic component > dynamic binding 1`] = `
-"import { resolveDynamicComponent as _resolveDynamicComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { createDynamicComponent as _createDynamicComponent } from 'vue';
export function render(_ctx) {
- const n0 = _createComponentWithFallback(_resolveDynamicComponent(_ctx.foo), null, null, true)
+ const n0 = _createDynamicComponent(() => (_ctx.foo), null, null, true)
return n0
}"
`;
exports[`compiler: element transform > dynamic component > dynamic binding shorthand 1`] = `
-"import { resolveDynamicComponent as _resolveDynamicComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { createDynamicComponent as _createDynamicComponent } from 'vue';
export function render(_ctx) {
- const n0 = _createComponentWithFallback(_resolveDynamicComponent(_ctx.is), null, null, true)
+ const n0 = _createDynamicComponent(() => (_ctx.is), null, null, true)
return n0
}"
`;
`<component :is="foo" />`,
)
expect(code).toMatchSnapshot()
- expect(helpers).toContain('resolveDynamicComponent')
+ expect(helpers).toContain('createDynamicComponent')
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
const { code, ir, helpers } =
compileWithElementTransform(`<component :is />`)
expect(code).toMatchSnapshot()
- expect(helpers).toContain('resolveDynamicComponent')
+ expect(helpers).toContain('createDynamicComponent')
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
...inlineHandlers,
`const n${operation.id} = `,
...genCall(
- operation.asset
- ? helper('createComponentWithFallback')
- : helper('createComponent'),
+ operation.dynamic && !operation.dynamic.isStatic
+ ? helper('createDynamicComponent')
+ : operation.asset
+ ? helper('createComponentWithFallback')
+ : helper('createComponent'),
tag,
rawProps,
rawSlots,
function genTag() {
if (operation.dynamic) {
- return genCall(
- helper('resolveDynamicComponent'),
- genExpression(operation.dynamic, context),
- )
+ if (operation.dynamic.isStatic) {
+ return genCall(
+ helper('resolveDynamicComponent'),
+ genExpression(operation.dynamic, context),
+ )
+ } else {
+ return ['() => (', ...genExpression(operation.dynamic, context), ')']
+ }
} else if (operation.asset) {
return toValidAssetId(operation.tag, 'component')
} else {
--- /dev/null
+import { shallowRef } from '@vue/reactivity'
+import { nextTick } from '@vue/runtime-dom'
+import { createDynamicComponent } from '../src'
+import { makeRender } from './_utils'
+
+const define = makeRender()
+
+describe('api: createDynamicComponent', () => {
+ const A = () => document.createTextNode('AAA')
+ const B = () => document.createTextNode('BBB')
+
+ test('direct value', async () => {
+ const val = shallowRef<any>(A)
+
+ const { html } = define({
+ setup() {
+ return createDynamicComponent(() => val.value)
+ },
+ }).render()
+
+ expect(html()).toBe('AAA<!--dynamic-component-->')
+
+ val.value = B
+ await nextTick()
+ expect(html()).toBe('BBB<!--dynamic-component-->')
+
+ // fallback
+ val.value = 'foo'
+ await nextTick()
+ expect(html()).toBe('<foo></foo><!--dynamic-component-->')
+ })
+
+ test('global registration', async () => {
+ const val = shallowRef('foo')
+
+ const { app, html, mount } = define({
+ setup() {
+ return createDynamicComponent(() => val.value)
+ },
+ }).create()
+
+ app.component('foo', A)
+ app.component('bar', B)
+
+ mount()
+ expect(html()).toBe('AAA<!--dynamic-component-->')
+
+ val.value = 'bar'
+ await nextTick()
+ expect(html()).toBe('BBB<!--dynamic-component-->')
+
+ // fallback
+ val.value = 'baz'
+ await nextTick()
+ expect(html()).toBe('<baz></baz><!--dynamic-component-->')
+ })
+})
--- /dev/null
+import { resolveDynamicComponent } from '@vue/runtime-dom'
+import { DynamicFragment, type Fragment } from './block'
+import { createComponentWithFallback } from './component'
+import { renderEffect } from './renderEffect'
+import type { RawProps } from './componentProps'
+import type { RawSlots } from './componentSlots'
+
+export function createDynamicComponent(
+ getter: () => any,
+ rawProps?: RawProps | null,
+ rawSlots?: RawSlots | null,
+ isSingleRoot?: boolean,
+): Fragment {
+ const frag = __DEV__
+ ? new DynamicFragment('dynamic-component')
+ : new DynamicFragment()
+ renderEffect(() => {
+ const value = getter()
+ frag.update(
+ () =>
+ createComponentWithFallback(
+ resolveDynamicComponent(value) as any,
+ rawProps,
+ rawSlots,
+ isSingleRoot,
+ ),
+ value,
+ )
+ })
+ return frag
+}
document.createTextNode('')
}
- update(render?: BlockFn): void {
- if (render === this.current) {
+ update(render?: BlockFn, key: any = render): void {
+ if (key === this.current) {
return
}
- this.current = render
+ this.current = key
pauseTracking()
const parent = this.anchor.parentNode
*/
export function createComponentWithFallback(
comp: VaporComponent | string,
- rawProps: RawProps | null | undefined,
- rawSlots: RawSlots | null | undefined,
+ rawProps?: RawProps | null,
+ rawSlots?: RawSlots | null,
isSingleRoot?: boolean,
): HTMLElement | VaporComponentInstance {
if (!isString(comp)) {
getDefaultValue,
} from './apiCreateFor'
export { createTemplateRefSetter } from './apiTemplateRef'
+export { createDynamicComponent } from './apiCreateDynamicComponent'