mirror of https://github.com/vuejs/core.git
wip(vapor): createIf
This commit is contained in:
parent
bcb9209c4c
commit
4318129b96
|
@ -169,10 +169,12 @@ describe('component: slots', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('slot should be rendered correctly with slot props', async () => {
|
test('slot should be rendered correctly with slot props', async () => {
|
||||||
|
const src = ref('header')
|
||||||
|
|
||||||
const Comp = defineVaporComponent(() => {
|
const Comp = defineVaporComponent(() => {
|
||||||
const n0 = template('<div></div>')()
|
const n0 = template('<div></div>')()
|
||||||
insert(
|
insert(
|
||||||
createSlot('header', { title: () => 'header' }),
|
createSlot('header', { title: () => src.value }),
|
||||||
n0 as any as ParentNode,
|
n0 as any as ParentNode,
|
||||||
)
|
)
|
||||||
return n0
|
return n0
|
||||||
|
@ -191,6 +193,10 @@ describe('component: slots', () => {
|
||||||
}).render()
|
}).render()
|
||||||
|
|
||||||
expect(host.innerHTML).toBe('<div><h1>header</h1><!--slot--></div>')
|
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 () => {
|
test('dynamic slot props', async () => {
|
||||||
|
@ -263,17 +269,23 @@ describe('component: slots', () => {
|
||||||
$: [
|
$: [
|
||||||
() => ({
|
() => ({
|
||||||
name: 'header',
|
name: 'header',
|
||||||
fn: (props: any) => template(props.title)(),
|
fn: (props: any) => {
|
||||||
|
const el = template('<h1></h1>')()
|
||||||
|
renderEffect(() => {
|
||||||
|
setText(el, props.title)
|
||||||
|
})
|
||||||
|
return el
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
}).render()
|
}).render()
|
||||||
|
|
||||||
expect(host.innerHTML).toBe('<div>header<!--slot--></div>')
|
expect(host.innerHTML).toBe('<div><h1>header</h1><!--slot--></div>')
|
||||||
|
|
||||||
val.value = 'footer'
|
val.value = 'footer'
|
||||||
await nextTick()
|
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 () => {
|
test('dynamic slot outlet should be render correctly with slot props', async () => {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { unmountComponent } from '../src/component'
|
||||||
|
|
||||||
const define = makeRender()
|
const define = makeRender()
|
||||||
|
|
||||||
describe.todo('createIf', () => {
|
describe('createIf', () => {
|
||||||
test('basic', async () => {
|
test('basic', async () => {
|
||||||
// mock this template:
|
// mock this template:
|
||||||
// <div>
|
// <div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { BlockFn, Fragment } from './block'
|
import { type BlockFn, DynamicFragment } from './block'
|
||||||
|
import { renderEffect } from './renderEffect'
|
||||||
|
|
||||||
export function createIf(
|
export function createIf(
|
||||||
condition: () => any,
|
condition: () => any,
|
||||||
|
@ -6,6 +7,12 @@ export function createIf(
|
||||||
b2?: BlockFn,
|
b2?: BlockFn,
|
||||||
once?: boolean,
|
once?: boolean,
|
||||||
// hydrationNode?: Node,
|
// hydrationNode?: Node,
|
||||||
): Fragment {
|
): DynamicFragment {
|
||||||
return [] as any
|
const frag = __DEV__ ? new DynamicFragment('if') : new DynamicFragment()
|
||||||
|
if (once) {
|
||||||
|
frag.update(condition() ? b1 : b2)
|
||||||
|
} else {
|
||||||
|
renderEffect(() => frag.update(condition() ? b1 : b2))
|
||||||
|
}
|
||||||
|
return frag
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
unmountComponent,
|
unmountComponent,
|
||||||
} from './component'
|
} from './component'
|
||||||
import { createComment } from './dom/node'
|
import { createComment } from './dom/node'
|
||||||
import { EffectScope } from '@vue/reactivity'
|
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||||
|
|
||||||
export type Block =
|
export type Block =
|
||||||
| Node
|
| Node
|
||||||
|
@ -29,7 +29,7 @@ export class Fragment {
|
||||||
export class DynamicFragment extends Fragment {
|
export class DynamicFragment extends Fragment {
|
||||||
anchor: Node
|
anchor: Node
|
||||||
scope: EffectScope | undefined
|
scope: EffectScope | undefined
|
||||||
key: any
|
current?: BlockFn
|
||||||
|
|
||||||
constructor(anchorLabel?: string) {
|
constructor(anchorLabel?: string) {
|
||||||
super([])
|
super([])
|
||||||
|
@ -40,10 +40,13 @@ export class DynamicFragment extends Fragment {
|
||||||
document.createTextNode('')
|
document.createTextNode('')
|
||||||
}
|
}
|
||||||
|
|
||||||
update(render?: BlockFn, key: any = render): void {
|
update(render?: BlockFn): void {
|
||||||
if (key === this.key) return
|
if (render === this.current) {
|
||||||
this.key = key
|
return
|
||||||
|
}
|
||||||
|
this.current = render
|
||||||
|
|
||||||
|
pauseTracking()
|
||||||
const parent = this.anchor.parentNode
|
const parent = this.anchor.parentNode
|
||||||
|
|
||||||
// teardown previous branch
|
// teardown previous branch
|
||||||
|
@ -60,6 +63,7 @@ export class DynamicFragment extends Fragment {
|
||||||
this.scope = undefined
|
this.scope = undefined
|
||||||
this.nodes = []
|
this.nodes = []
|
||||||
}
|
}
|
||||||
|
resetTracking()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,26 +109,28 @@ export function createSlot(
|
||||||
fallback?: Slot,
|
fallback?: Slot,
|
||||||
): Block {
|
): Block {
|
||||||
const instance = currentInstance as VaporComponentInstance
|
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
|
const slotProps = rawProps
|
||||||
? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers)
|
? new Proxy(rawProps, dynamicSlotsPropsProxyHandlers)
|
||||||
: EMPTY_OBJ
|
: EMPTY_OBJ
|
||||||
|
|
||||||
// always create effect because a slot may contain dynamic root inside
|
const renderSlot = (name: string) => {
|
||||||
// which affects fallback
|
const slot = getSlot(rawSlots, name)
|
||||||
renderEffect(() => {
|
|
||||||
const slot = getSlot(instance.rawSlots, isFunction(name) ? name() : name)
|
|
||||||
if (slot) {
|
if (slot) {
|
||||||
fragment.update(
|
fragment.update(() => slot(slotProps) || (fallback && fallback()))
|
||||||
() => slot(slotProps) || (fallback && fallback()),
|
|
||||||
// TODO this key needs to account for possible fallback (v-if)
|
|
||||||
// inside the slot
|
|
||||||
slot,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
fragment.update(fallback)
|
fragment.update(fallback)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// dynamic slot name or has dynamicSlots
|
||||||
|
if (isDynamicName || rawSlots.$) {
|
||||||
|
renderEffect(() => renderSlot(isFunction(name) ? name() : name))
|
||||||
|
} else {
|
||||||
|
renderSlot(name)
|
||||||
|
}
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue