mirror of https://github.com/vuejs/core.git
wip(vapor): vapor slots in vdom
This commit is contained in:
parent
700562866b
commit
23939d09c6
|
@ -192,6 +192,8 @@ export interface VaporInteropInterface {
|
|||
update(n1: VNode, n2: VNode, shouldUpdate: boolean): void
|
||||
unmount(vnode: VNode, doRemove?: boolean): void
|
||||
move(vnode: VNode, container: any, anchor: any): void
|
||||
slot(n1: VNode | null, n2: VNode, container: any, anchor: any): void
|
||||
|
||||
vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any
|
||||
vdomUnmount: UnmountComponentFn
|
||||
vdomSlot: (
|
||||
|
|
|
@ -837,8 +837,8 @@ export function setupComponent(
|
|||
vi(instance)
|
||||
} else {
|
||||
initProps(instance, props, isStateful, isSSR)
|
||||
initSlots(instance, children, optimized)
|
||||
}
|
||||
initSlots(instance, children, optimized)
|
||||
|
||||
const setupResult = isStateful
|
||||
? setupStatefulComponent(instance, isSSR)
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
Fragment,
|
||||
type VNode,
|
||||
type VNodeArrayChildren,
|
||||
VaporSlot,
|
||||
createBlock,
|
||||
createVNode,
|
||||
isVNode,
|
||||
|
@ -31,6 +32,15 @@ export function renderSlot(
|
|||
fallback?: () => VNodeArrayChildren,
|
||||
noSlotted?: boolean,
|
||||
): VNode {
|
||||
let slot = slots[name]
|
||||
|
||||
// vapor slots rendered in vdom
|
||||
if (slot && slots._vapor) {
|
||||
const ret = (openBlock(), createBlock(VaporSlot, props))
|
||||
ret.vs = { slot, fallback }
|
||||
return ret
|
||||
}
|
||||
|
||||
if (
|
||||
currentRenderingInstance &&
|
||||
(currentRenderingInstance.ce ||
|
||||
|
@ -53,8 +63,6 @@ export function renderSlot(
|
|||
)
|
||||
}
|
||||
|
||||
let slot = slots[name]
|
||||
|
||||
if (__DEV__ && slot && slot.length > 1) {
|
||||
warn(
|
||||
`SSR-optimized slot function detected in a non-SSR-optimized render ` +
|
||||
|
|
|
@ -505,7 +505,7 @@ export { type VaporInteropInterface } from './apiCreateApp'
|
|||
/**
|
||||
* @internal
|
||||
*/
|
||||
export { type RendererInternals } from './renderer'
|
||||
export { type RendererInternals, MoveType } from './renderer'
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
type VNodeArrayChildren,
|
||||
type VNodeHook,
|
||||
type VNodeProps,
|
||||
VaporSlot,
|
||||
cloneIfMounted,
|
||||
cloneVNode,
|
||||
createVNode,
|
||||
|
@ -445,6 +446,9 @@ function baseCreateRenderer(
|
|||
optimized,
|
||||
)
|
||||
break
|
||||
case VaporSlot:
|
||||
getVaporInterface(parentComponent, n2).slot(n1, n2, container, anchor)
|
||||
break
|
||||
default:
|
||||
if (shapeFlag & ShapeFlags.ELEMENT) {
|
||||
processElement(
|
||||
|
@ -2186,6 +2190,7 @@ function baseCreateRenderer(
|
|||
if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||
if ((type as ConcreteComponent).__vapor) {
|
||||
getVaporInterface(parentComponent, vnode).unmount(vnode, doRemove)
|
||||
return
|
||||
} else {
|
||||
unmountComponent(vnode.component!, parentSuspense, doRemove)
|
||||
}
|
||||
|
@ -2236,6 +2241,11 @@ function baseCreateRenderer(
|
|||
unmountChildren(children as VNode[], parentComponent, parentSuspense)
|
||||
}
|
||||
|
||||
if (type === VaporSlot) {
|
||||
getVaporInterface(parentComponent, vnode).unmount(vnode, doRemove)
|
||||
return
|
||||
}
|
||||
|
||||
if (doRemove) {
|
||||
remove(vnode)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import type { RawSlots } from './componentSlots'
|
|||
import {
|
||||
type ReactiveFlags,
|
||||
type Ref,
|
||||
type ShallowRef,
|
||||
isProxy,
|
||||
isRef,
|
||||
toRaw,
|
||||
|
@ -70,6 +71,7 @@ export const Fragment = Symbol.for('v-fgt') as any as {
|
|||
export const Text: unique symbol = Symbol.for('v-txt')
|
||||
export const Comment: unique symbol = Symbol.for('v-cmt')
|
||||
export const Static: unique symbol = Symbol.for('v-stc')
|
||||
export const VaporSlot: unique symbol = Symbol.for('v-vps')
|
||||
|
||||
export type VNodeTypes =
|
||||
| string
|
||||
|
@ -83,6 +85,7 @@ export type VNodeTypes =
|
|||
| typeof TeleportImpl
|
||||
| typeof Suspense
|
||||
| typeof SuspenseImpl
|
||||
| typeof VaporSlot
|
||||
|
||||
export type VNodeRef =
|
||||
| string
|
||||
|
@ -258,6 +261,18 @@ export interface VNode<
|
|||
* @internal VDOM in Vapor interop hook
|
||||
*/
|
||||
vi?: (instance: ComponentInternalInstance) => void
|
||||
/**
|
||||
* @internal Vapor slot in VDOM metadata
|
||||
*/
|
||||
vs?: {
|
||||
slot: (props: any) => any
|
||||
fallback: (() => VNodeArrayChildren) | undefined
|
||||
ref?: ShallowRef<any>
|
||||
}
|
||||
/**
|
||||
* @internal Vapor slot Block
|
||||
*/
|
||||
vb?: any
|
||||
}
|
||||
|
||||
// Since v-if and v-for are the two possible ways node structure can dynamically
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
mountComponent,
|
||||
unmountComponent,
|
||||
} from './component'
|
||||
import { createComment } from './dom/node'
|
||||
import { createComment, createTextNode } from './dom/node'
|
||||
import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity'
|
||||
|
||||
export type Block =
|
||||
|
@ -37,9 +37,7 @@ export class DynamicFragment extends VaporFragment {
|
|||
constructor(anchorLabel?: string) {
|
||||
super([])
|
||||
this.anchor =
|
||||
__DEV__ && anchorLabel
|
||||
? createComment(anchorLabel)
|
||||
: document.createTextNode('')
|
||||
__DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
|
||||
}
|
||||
|
||||
update(render?: BlockFn, key: any = render): void {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
|
||||
import { type Block, type BlockFn, DynamicFragment } from './block'
|
||||
import { rawPropsProxyHandlers } from './componentProps'
|
||||
import { currentInstance } from '@vue/runtime-core'
|
||||
import { currentInstance, isRef } from '@vue/runtime-dom'
|
||||
import type { LooseRawProps, VaporComponentInstance } from './component'
|
||||
import { renderEffect } from './renderEffect'
|
||||
|
||||
|
@ -96,7 +96,7 @@ export function createSlot(
|
|||
? new Proxy(rawProps, rawPropsProxyHandlers)
|
||||
: EMPTY_OBJ
|
||||
|
||||
if (rawSlots._) {
|
||||
if (isRef(rawSlots._)) {
|
||||
return instance.appContext.vapor!.vdomSlot(
|
||||
rawSlots._,
|
||||
name,
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
popWarningContext,
|
||||
pushWarningContext,
|
||||
simpleSetCurrentInstance,
|
||||
} from '@vue/runtime-core'
|
||||
} from '@vue/runtime-dom'
|
||||
import { insert, normalizeBlock, remove } from './block'
|
||||
import {
|
||||
type VaporComponent,
|
||||
|
|
|
@ -14,8 +14,8 @@ import { invokeArrayFns } from '@vue/shared'
|
|||
export function renderEffect(fn: () => void, noLifecycle = false): void {
|
||||
const instance = currentInstance as VaporComponentInstance | null
|
||||
const scope = getCurrentScope()
|
||||
if (__DEV__ && !__TEST__ && !isVaporComponent(instance)) {
|
||||
warn('renderEffect called without active vapor instance.')
|
||||
if (__DEV__ && !__TEST__ && !scope && !isVaporComponent(instance)) {
|
||||
warn('renderEffect called without active EffectScope or Vapor instance.')
|
||||
}
|
||||
|
||||
// renderEffect is always called after user has registered all hooks
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
type ComponentInternalInstance,
|
||||
type ConcreteComponent,
|
||||
MoveType,
|
||||
type Plugin,
|
||||
type RendererInternals,
|
||||
type ShallowRef,
|
||||
|
@ -10,6 +11,7 @@ import {
|
|||
createVNode,
|
||||
currentInstance,
|
||||
ensureRenderer,
|
||||
onScopeDispose,
|
||||
renderSlot,
|
||||
shallowRef,
|
||||
simpleSetCurrentInstance,
|
||||
|
@ -24,17 +26,19 @@ import {
|
|||
unmountComponent,
|
||||
} from './component'
|
||||
import { type Block, VaporFragment, insert, remove } from './block'
|
||||
import { extend, isFunction } from '@vue/shared'
|
||||
import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
|
||||
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
|
||||
import type { RawSlots, VaporSlot } from './componentSlots'
|
||||
import { renderEffect } from './renderEffect'
|
||||
import { createTextNode } from './dom/node'
|
||||
|
||||
// mounting vapor components and slots in vdom
|
||||
const vaporInteropImpl: Omit<
|
||||
VaporInteropInterface,
|
||||
'vdomMount' | 'vdomUnmount' | 'vdomSlot'
|
||||
> = {
|
||||
mount(vnode, container, anchor, parentComponent) {
|
||||
const selfAnchor = (vnode.anchor = document.createComment('vapor'))
|
||||
const selfAnchor = (vnode.el = vnode.anchor = createTextNode())
|
||||
container.insertBefore(selfAnchor, anchor)
|
||||
const prev = currentInstance
|
||||
simpleSetCurrentInstance(parentComponent)
|
||||
|
@ -61,6 +65,7 @@ const vaporInteropImpl: Omit<
|
|||
|
||||
update(n1, n2, shouldUpdate) {
|
||||
n2.component = n1.component
|
||||
n2.el = n2.anchor = n1.anchor
|
||||
if (shouldUpdate) {
|
||||
const instance = n2.component as any as VaporComponentInstance
|
||||
instance.rawPropsRef!.value = n2.props
|
||||
|
@ -70,15 +75,71 @@ const vaporInteropImpl: Omit<
|
|||
|
||||
unmount(vnode, doRemove) {
|
||||
const container = doRemove ? vnode.anchor!.parentNode : undefined
|
||||
unmountComponent(vnode.component as any, container)
|
||||
if (vnode.component) {
|
||||
unmountComponent(vnode.component as any, container)
|
||||
} else if (vnode.vb) {
|
||||
remove(vnode.vb, container)
|
||||
}
|
||||
remove(vnode.anchor as Node, container)
|
||||
},
|
||||
|
||||
/**
|
||||
* vapor slot in vdom
|
||||
*/
|
||||
slot(n1: VNode, n2: VNode, container, anchor) {
|
||||
if (!n1) {
|
||||
// mount
|
||||
const selfAnchor = (n2.el = n2.anchor = createTextNode())
|
||||
insert(selfAnchor, container, anchor)
|
||||
const { slot, fallback } = n2.vs!
|
||||
const propsRef = (n2.vs!.ref = shallowRef(n2.props))
|
||||
const slotBlock = slot(new Proxy(propsRef, vaporSlotPropsProxyHandler))
|
||||
// TODO fallback for slot with v-if content
|
||||
// fallback is a vnode slot function here, and slotBlock, if a DynamicFragment,
|
||||
// expects a Vapor BlockFn as fallback
|
||||
fallback
|
||||
insert((n2.vb = slotBlock), container, selfAnchor)
|
||||
} else {
|
||||
// update
|
||||
n2.el = n2.anchor = n1.anchor
|
||||
n2.vb = n1.vb
|
||||
;(n2.vs!.ref = n1.vs!.ref)!.value = n2.props
|
||||
}
|
||||
},
|
||||
|
||||
move(vnode, container, anchor) {
|
||||
insert(vnode.component as any, container, anchor)
|
||||
insert(vnode.vb || (vnode.component as any), container, anchor)
|
||||
insert(vnode.anchor as any, container, anchor)
|
||||
},
|
||||
}
|
||||
|
||||
const vaporSlotPropsProxyHandler: ProxyHandler<
|
||||
ShallowRef<Record<string, any>>
|
||||
> = {
|
||||
get(target, key: any) {
|
||||
return target.value[key]
|
||||
},
|
||||
has(target, key: any) {
|
||||
return target.value[key]
|
||||
},
|
||||
ownKeys(target) {
|
||||
return Object.keys(target.value)
|
||||
},
|
||||
}
|
||||
|
||||
const vaporSlotsProxyHandler: ProxyHandler<any> = {
|
||||
get(target, key) {
|
||||
if (key === '_vapor') {
|
||||
return target
|
||||
} else {
|
||||
return target[key]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount vdom component in vapor
|
||||
*/
|
||||
function createVDOMComponent(
|
||||
internals: RendererInternals,
|
||||
component: ConcreteComponent,
|
||||
|
@ -100,35 +161,51 @@ function createVDOMComponent(
|
|||
vnode.vi = (instance: ComponentInternalInstance) => {
|
||||
instance.props = wrapper.props
|
||||
instance.attrs = wrapper.attrs
|
||||
// TODO slots
|
||||
instance.slots =
|
||||
wrapper.slots === EMPTY_OBJ
|
||||
? EMPTY_OBJ
|
||||
: new Proxy(wrapper.slots, vaporSlotsProxyHandler)
|
||||
}
|
||||
|
||||
let isMounted = false
|
||||
const parentInstance = currentInstance as VaporComponentInstance
|
||||
frag.insert = (parent, anchor) => {
|
||||
const unmount = (parentNode?: ParentNode) => {
|
||||
internals.umt(vnode.component!, null, !!parentNode)
|
||||
}
|
||||
|
||||
frag.insert = (parentNode, anchor) => {
|
||||
if (!isMounted) {
|
||||
internals.mt(
|
||||
vnode,
|
||||
parent,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentInstance as any,
|
||||
null,
|
||||
undefined,
|
||||
false,
|
||||
)
|
||||
// TODO register unmount with onScopeDispose
|
||||
onScopeDispose(unmount, true)
|
||||
isMounted = true
|
||||
} else {
|
||||
// TODO move
|
||||
// move
|
||||
internals.m(
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
MoveType.REORDER,
|
||||
parentInstance as any,
|
||||
)
|
||||
}
|
||||
}
|
||||
frag.remove = parentNode => {
|
||||
internals.umt(vnode.component!, null, !!parentNode)
|
||||
}
|
||||
|
||||
frag.remove = unmount
|
||||
|
||||
return frag
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount vdom slot in vapor
|
||||
*/
|
||||
function renderVDOMSlot(
|
||||
internals: RendererInternals,
|
||||
slotsRef: ShallowRef<Slots>,
|
||||
|
@ -156,7 +233,13 @@ function renderVDOMSlot(
|
|||
remove(fallbackNodes, parentNode)
|
||||
fallbackNodes = undefined
|
||||
}
|
||||
internals.p(oldVNode, vnode, parent, anchor, parentComponent as any)
|
||||
internals.p(
|
||||
oldVNode,
|
||||
vnode,
|
||||
parentNode,
|
||||
anchor,
|
||||
parentComponent as any,
|
||||
)
|
||||
oldVNode = vnode
|
||||
} else {
|
||||
if (fallback && !fallbackNodes) {
|
||||
|
@ -171,7 +254,14 @@ function renderVDOMSlot(
|
|||
})
|
||||
isMounted = true
|
||||
} else {
|
||||
// TODO move
|
||||
// move
|
||||
internals.m(
|
||||
oldVNode!,
|
||||
parentNode,
|
||||
anchor,
|
||||
MoveType.REORDER,
|
||||
parentComponent as any,
|
||||
)
|
||||
}
|
||||
|
||||
frag.remove = parentNode => {
|
||||
|
|
Loading…
Reference in New Issue