wip(vapor): createIf

This commit is contained in:
Evan You 2024-12-14 20:37:43 +08:00
parent bcb9209c4c
commit 4318129b96
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
5 changed files with 50 additions and 25 deletions

View File

@ -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('<div></div>')()
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('<div><h1>header</h1><!--slot--></div>')
src.value = 'footer'
await nextTick()
expect(host.innerHTML).toBe('<div><h1>footer</h1><!--slot--></div>')
})
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('<h1></h1>')()
renderEffect(() => {
setText(el, props.title)
})
return el
},
}),
],
})
}).render()
expect(host.innerHTML).toBe('<div>header<!--slot--></div>')
expect(host.innerHTML).toBe('<div><h1>header</h1><!--slot--></div>')
val.value = 'footer'
await nextTick()
expect(host.innerHTML).toBe('<div>footer<!--slot--></div>')
expect(host.innerHTML).toBe('<div><h1>footer</h1><!--slot--></div>')
})
test('dynamic slot outlet should be render correctly with slot props', async () => {

View File

@ -15,7 +15,7 @@ import { unmountComponent } from '../src/component'
const define = makeRender()
describe.todo('createIf', () => {
describe('createIf', () => {
test('basic', async () => {
// mock this template:
// <div>

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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
}