diff --git a/packages/runtime-vapor/__tests__/componentSlots.spec.ts b/packages/runtime-vapor/__tests__/componentSlots.spec.ts index 78be11ebe..868e45671 100644 --- a/packages/runtime-vapor/__tests__/componentSlots.spec.ts +++ b/packages/runtime-vapor/__tests__/componentSlots.spec.ts @@ -169,10 +169,12 @@ describe('component: slots', () => { }) test('slot should be rendered correctly with slot props', async () => { + const src = ref('header') + const Comp = defineVaporComponent(() => { const n0 = template('
')() insert( - createSlot('header', { title: () => 'header' }), + createSlot('header', { title: () => src.value }), n0 as any as ParentNode, ) return n0 @@ -191,6 +193,10 @@ describe('component: slots', () => { }).render() expect(host.innerHTML).toBe('

header

') + + src.value = 'footer' + await nextTick() + expect(host.innerHTML).toBe('

footer

') }) test('dynamic slot props', async () => { @@ -263,17 +269,23 @@ describe('component: slots', () => { $: [ () => ({ name: 'header', - fn: (props: any) => template(props.title)(), + fn: (props: any) => { + const el = template('

')() + renderEffect(() => { + setText(el, props.title) + }) + return el + }, }), ], }) }).render() - expect(host.innerHTML).toBe('
header
') + expect(host.innerHTML).toBe('

header

') val.value = 'footer' await nextTick() - expect(host.innerHTML).toBe('
footer
') + expect(host.innerHTML).toBe('

footer

') }) test('dynamic slot outlet should be render correctly with slot props', async () => { diff --git a/packages/runtime-vapor/__tests__/if.spec.ts b/packages/runtime-vapor/__tests__/if.spec.ts index 7acff485a..a7e6266a7 100644 --- a/packages/runtime-vapor/__tests__/if.spec.ts +++ b/packages/runtime-vapor/__tests__/if.spec.ts @@ -15,7 +15,7 @@ import { unmountComponent } from '../src/component' const define = makeRender() -describe.todo('createIf', () => { +describe('createIf', () => { test('basic', async () => { // mock this template: //
diff --git a/packages/runtime-vapor/src/apiCreateIf.ts b/packages/runtime-vapor/src/apiCreateIf.ts index 7e5661124..e40353139 100644 --- a/packages/runtime-vapor/src/apiCreateIf.ts +++ b/packages/runtime-vapor/src/apiCreateIf.ts @@ -1,4 +1,5 @@ -import type { BlockFn, Fragment } from './block' +import { type BlockFn, DynamicFragment } from './block' +import { renderEffect } from './renderEffect' export function createIf( condition: () => any, @@ -6,6 +7,12 @@ export function createIf( b2?: BlockFn, once?: boolean, // hydrationNode?: Node, -): Fragment { - return [] as any +): DynamicFragment { + const frag = __DEV__ ? new DynamicFragment('if') : new DynamicFragment() + if (once) { + frag.update(condition() ? b1 : b2) + } else { + renderEffect(() => frag.update(condition() ? b1 : b2)) + } + return frag } diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 65b7c70de..eccd57332 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -6,7 +6,7 @@ import { unmountComponent, } from './component' import { createComment } from './dom/node' -import { EffectScope } from '@vue/reactivity' +import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity' export type Block = | Node @@ -29,7 +29,7 @@ export class Fragment { export class DynamicFragment extends Fragment { anchor: Node scope: EffectScope | undefined - key: any + current?: BlockFn constructor(anchorLabel?: string) { super([]) @@ -40,10 +40,13 @@ export class DynamicFragment extends Fragment { document.createTextNode('') } - update(render?: BlockFn, key: any = render): void { - if (key === this.key) return - this.key = key + update(render?: BlockFn): void { + if (render === this.current) { + return + } + this.current = render + pauseTracking() const parent = this.anchor.parentNode // teardown previous branch @@ -60,6 +63,7 @@ export class DynamicFragment extends Fragment { this.scope = undefined this.nodes = [] } + resetTracking() } } diff --git a/packages/runtime-vapor/src/componentSlots.ts b/packages/runtime-vapor/src/componentSlots.ts index 6eb365e00..33d5ff233 100644 --- a/packages/runtime-vapor/src/componentSlots.ts +++ b/packages/runtime-vapor/src/componentSlots.ts @@ -109,26 +109,28 @@ export function createSlot( fallback?: Slot, ): Block { const instance = currentInstance as VaporComponentInstance - const fragment = new DynamicFragment('slot') + const rawSlots = instance.rawSlots + const isDynamicName = isFunction(name) + const fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment() const slotProps = rawProps ? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers) : EMPTY_OBJ - // always create effect because a slot may contain dynamic root inside - // which affects fallback - renderEffect(() => { - const slot = getSlot(instance.rawSlots, isFunction(name) ? name() : name) + const renderSlot = (name: string) => { + const slot = getSlot(rawSlots, name) if (slot) { - fragment.update( - () => slot(slotProps) || (fallback && fallback()), - // TODO this key needs to account for possible fallback (v-if) - // inside the slot - slot, - ) + fragment.update(() => slot(slotProps) || (fallback && fallback())) } else { fragment.update(fallback) } - }) + } + + // dynamic slot name or has dynamicSlots + if (isDynamicName || rawSlots.$) { + renderEffect(() => renderSlot(isFunction(name) ? name() : name)) + } else { + renderSlot(name) + } return fragment }