Skip to content

Commit

Permalink
wip(vapor): vdom slots in vapor component
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Feb 6, 2025
1 parent a770a83 commit 99d70dd
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 32 deletions.
7 changes: 7 additions & 0 deletions packages/runtime-core/src/apiCreateApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ export interface VaporInteropInterface {
move(vnode: VNode, container: any, anchor: any): void
vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any
vdomUnmount: UnmountComponentFn
vdomSlot: (
slots: any,
name: string | (() => string),
props: Record<string, any>,
parentComponent: any, // VaporComponentInstance
fallback?: any, // VaporSlot
) => any
}

/**
Expand Down
9 changes: 5 additions & 4 deletions packages/runtime-core/src/helpers/renderSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ export function renderSlot(
noSlotted?: boolean,
): VNode {
if (
currentRenderingInstance!.ce ||
(currentRenderingInstance!.parent &&
isAsyncWrapper(currentRenderingInstance!.parent) &&
currentRenderingInstance!.parent.ce)
currentRenderingInstance &&
(currentRenderingInstance.ce ||
(currentRenderingInstance.parent &&
isAsyncWrapper(currentRenderingInstance.parent) &&
currentRenderingInstance.parent.ce))
) {
// in custom element mode, render <slot/> as actual slot outlets
// wrap it with a fragment because in shadowRoot: false mode the slot
Expand Down
10 changes: 7 additions & 3 deletions packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type GenericComponentInstance,
isClassComponent,
} from './component'
import { queueJob, queuePostFlushCb } from './scheduler'
import { nextTick, queueJob, queuePostFlushCb } from './scheduler'
import { extend, getGlobalThis } from '@vue/shared'

type HMRComponent = ComponentOptions | ClassComponent
Expand Down Expand Up @@ -102,7 +102,9 @@ function rerender(id: string, newRender?: Function): void {
i.renderCache = []
i.update()
}
isHmrUpdating = false
nextTick(() => {
isHmrUpdating = false
})
})
}

Expand Down Expand Up @@ -160,7 +162,9 @@ function reload(id: string, newComp: HMRComponent): void {
} else {
;(parent as ComponentInternalInstance).update()
}
isHmrUpdating = false
nextTick(() => {
isHmrUpdating = false
})
// #6930, #11248 avoid infinite recursion
dirtyInstances.delete(instance)
})
Expand Down
16 changes: 9 additions & 7 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2530,15 +2530,17 @@ function resolveChildrenNamespace(
}

function toggleRecurse(
{ effect, job }: ComponentInternalInstance,
{ effect, job, vapor }: ComponentInternalInstance,
allowed: boolean,
) {
if (allowed) {
effect.flags |= EffectFlags.ALLOW_RECURSE
job.flags! |= SchedulerJobFlags.ALLOW_RECURSE
} else {
effect.flags &= ~EffectFlags.ALLOW_RECURSE
job.flags! &= ~SchedulerJobFlags.ALLOW_RECURSE
if (!vapor) {
if (allowed) {
effect.flags |= EffectFlags.ALLOW_RECURSE
job.flags! |= SchedulerJobFlags.ALLOW_RECURSE
} else {
effect.flags &= ~EffectFlags.ALLOW_RECURSE
job.flags! &= ~SchedulerJobFlags.ALLOW_RECURSE
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,13 @@ export class VaporComponentInstance implements GenericComponentInstance {
props: Record<string, any>
attrs: Record<string, any>
propsDefaults: Record<string, any> | null
rawPropsRef?: ShallowRef<any> // to hold vnode props in vdom interop mode

slots: StaticSlots

// to hold vnode props / slots in vdom interop mode
rawPropsRef?: ShallowRef<any>
rawSlotsRef?: ShallowRef<any>

emit: EmitFn
emitted: Record<string, boolean> | null

Expand Down
18 changes: 12 additions & 6 deletions packages/runtime-vapor/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,23 +85,29 @@ export function getSlot(
}
}

// TODO how to handle empty slot return blocks?
// e.g. a slot renders a v-if node that may toggle inside.
// we may need special handling by passing the fallback into the slot
// and make the v-if use it as fallback
export function createSlot(
name: string | (() => string),
rawProps?: LooseRawProps | null,
fallback?: VaporSlot,
): Block {
const instance = currentInstance as VaporComponentInstance
const rawSlots = instance.rawSlots
const isDynamicName = isFunction(name)
const fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
const slotProps = rawProps
? new Proxy(rawProps, rawPropsProxyHandlers)
: EMPTY_OBJ

if (rawSlots._) {
return instance.appContext.vapor!.vdomSlot(
rawSlots._,
name,
slotProps,
instance,
fallback,
)
}

const isDynamicName = isFunction(name)
const fragment = __DEV__ ? new DynamicFragment('slot') : new DynamicFragment()
const renderSlot = () => {
const slot = getSlot(rawSlots, isFunction(name) ? name() : name)
if (slot) {
Expand Down
98 changes: 87 additions & 11 deletions packages/runtime-vapor/src/vdomInterop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,57 @@ import {
type ConcreteComponent,
type Plugin,
type RendererInternals,
type ShallowRef,
type Slots,
type VNode,
type VaporInteropInterface,
createVNode,
currentInstance,
ensureRenderer,
renderSlot,
shallowRef,
simpleSetCurrentInstance,
} from '@vue/runtime-dom'
import {
type LooseRawProps,
type LooseRawSlots,
type VaporComponent,
VaporComponentInstance,
createComponent,
mountComponent,
unmountComponent,
} from './component'
import { VaporFragment, insert } from './block'
import { extend, remove } from '@vue/shared'
import { type Block, VaporFragment, insert, remove } from './block'
import { extend, isFunction, remove as removeItem } from '@vue/shared'
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
import type { RawSlots } from './componentSlots'
import type { RawSlots, VaporSlot } from './componentSlots'
import { renderEffect } from './renderEffect'

const vaporInteropImpl: Omit<
VaporInteropInterface,
'vdomMount' | 'vdomUnmount'
'vdomMount' | 'vdomUnmount' | 'vdomSlot'
> = {
mount(vnode, container, anchor, parentComponent) {
const selfAnchor = (vnode.anchor = document.createComment('vapor'))
container.insertBefore(selfAnchor, anchor)
const prev = currentInstance
simpleSetCurrentInstance(parentComponent)

const propsRef = shallowRef(vnode.props)
const slotsRef = shallowRef(vnode.children)

// @ts-expect-error
const instance = (vnode.component = createComponent(vnode.type, {
$: [() => propsRef.value],
}))
const instance = (vnode.component = createComponent(
vnode.type as any as VaporComponent,
{
$: [() => propsRef.value],
} as RawProps,
{
_: slotsRef, // pass the slots ref
} as any as RawSlots,
))
instance.rawPropsRef = propsRef
instance.rawSlotsRef = slotsRef
mountComponent(instance, container, selfAnchor)
simpleSetCurrentInstance(prev)
return instance
Expand All @@ -46,8 +62,9 @@ const vaporInteropImpl: Omit<
update(n1, n2, shouldUpdate) {
n2.component = n1.component
if (shouldUpdate) {
;(n2.component as any as VaporComponentInstance).rawPropsRef!.value =
n2.props
const instance = n2.component as any as VaporComponentInstance
instance.rawPropsRef!.value = n2.props
instance.rawSlotsRef!.value = n2.children
}
},

Expand Down Expand Up @@ -109,8 +126,66 @@ function createVDOMComponent(
}
frag.remove = () => {
internals.umt(vnode.component!, null, true)
remove(parentInstance.vdomChildren!, vnode.component)
isMounted = false
removeItem(parentInstance.vdomChildren!, vnode.component)
}

return frag
}

function renderVDOMSlot(
internals: RendererInternals,
slotsRef: ShallowRef<Slots>,
name: string | (() => string),
props: Record<string, any>,
parentComponent: VaporComponentInstance,
fallback?: VaporSlot,
): VaporFragment {
const frag = new VaporFragment([])

let isMounted = false
let fallbackNodes: Block | undefined
let parentNode: ParentNode
let oldVNode: VNode | null = null

frag.insert = (parent, anchor) => {
parentNode = parent
if (!isMounted) {
renderEffect(() => {
const vnode = renderSlot(
slotsRef.value,
isFunction(name) ? name() : name,
props,
)
if ((vnode.children as any[]).length) {
if (fallbackNodes) {
remove(fallbackNodes, parentNode)
fallbackNodes = undefined
}
internals.p(oldVNode, vnode, parent, anchor, parentComponent as any)
oldVNode = vnode
} else {
if (fallback && !fallbackNodes) {
// mount fallback
if (oldVNode) {
internals.um(oldVNode, parentComponent as any, null, true)
}
insert((fallbackNodes = fallback(props)), parent, anchor)
}
oldVNode = null
}
})
isMounted = true
} else {
// TODO move
}

frag.remove = () => {
if (fallbackNodes) {
remove(fallbackNodes, parentNode)
} else if (oldVNode) {
internals.um(oldVNode, parentComponent as any, null)
}
}
}

return frag
Expand All @@ -121,5 +196,6 @@ export const vaporInteropPlugin: Plugin = app => {
app._context.vapor = extend(vaporInteropImpl, {
vdomMount: createVDOMComponent.bind(null, internals),
vdomUnmount: internals.umt,
vdomSlot: renderVDOMSlot.bind(null, internals),
})
}

0 comments on commit 99d70dd

Please sign in to comment.