mirror of https://github.com/vuejs/core.git
feat(teleport): support deferred Teleport (#11387)
close #2015 close #11386
This commit is contained in:
parent
3ba70e49b5
commit
59a3e88903
File diff suppressed because it is too large
Load Diff
|
@ -618,7 +618,7 @@ describe('renderer: optimized mode', () => {
|
|||
})
|
||||
|
||||
//#3623
|
||||
test('nested teleport unmount need exit the optimization mode', () => {
|
||||
test('nested teleport unmount need exit the optimization mode', async () => {
|
||||
const target = nodeOps.createElement('div')
|
||||
const root = nodeOps.createElement('div')
|
||||
|
||||
|
@ -647,6 +647,7 @@ describe('renderer: optimized mode', () => {
|
|||
])),
|
||||
root,
|
||||
)
|
||||
await nextTick()
|
||||
expect(inner(target)).toMatchInlineSnapshot(
|
||||
`"<div><!--teleport start--><!--teleport end--></div><div>foo</div>"`,
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
type RendererInternals,
|
||||
type RendererNode,
|
||||
type RendererOptions,
|
||||
queuePostRenderEffect,
|
||||
traverseStaticChildren,
|
||||
} from '../renderer'
|
||||
import type { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
|
||||
|
@ -19,6 +20,7 @@ export type TeleportVNode = VNode<RendererNode, RendererElement, TeleportProps>
|
|||
export interface TeleportProps {
|
||||
to: string | RendererElement | null | undefined
|
||||
disabled?: boolean
|
||||
defer?: boolean
|
||||
}
|
||||
|
||||
export const TeleportEndKey = Symbol('_vte')
|
||||
|
@ -28,6 +30,9 @@ export const isTeleport = (type: any): boolean => type.__isTeleport
|
|||
const isTeleportDisabled = (props: VNode['props']): boolean =>
|
||||
props && (props.disabled || props.disabled === '')
|
||||
|
||||
const isTeleportDeferred = (props: VNode['props']): boolean =>
|
||||
props && (props.defer || props.defer === '')
|
||||
|
||||
const isTargetSVG = (target: RendererElement): boolean =>
|
||||
typeof SVGElement !== 'undefined' && target instanceof SVGElement
|
||||
|
||||
|
@ -107,7 +112,6 @@ export const TeleportImpl = {
|
|||
const mainAnchor = (n2.anchor = __DEV__
|
||||
? createComment('teleport end')
|
||||
: createText(''))
|
||||
const target = (n2.target = resolveTarget(n2.props, querySelector))
|
||||
const targetStart = (n2.targetStart = createText(''))
|
||||
const targetAnchor = (n2.targetAnchor = createText(''))
|
||||
insert(placeholder, container, anchor)
|
||||
|
@ -115,18 +119,6 @@ export const TeleportImpl = {
|
|||
// attach a special property so we can skip teleported content in
|
||||
// renderer's nextSibling search
|
||||
targetStart[TeleportEndKey] = targetAnchor
|
||||
if (target) {
|
||||
insert(targetStart, target)
|
||||
insert(targetAnchor, target)
|
||||
// #2652 we could be teleporting from a non-SVG tree into an SVG tree
|
||||
if (namespace === 'svg' || isTargetSVG(target)) {
|
||||
namespace = 'svg'
|
||||
} else if (namespace === 'mathml' || isTargetMathML(target)) {
|
||||
namespace = 'mathml'
|
||||
}
|
||||
} else if (__DEV__ && !disabled) {
|
||||
warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
|
||||
}
|
||||
|
||||
const mount = (container: RendererElement, anchor: RendererNode) => {
|
||||
// Teleport *always* has Array children. This is enforced in both the
|
||||
|
@ -145,10 +137,39 @@ export const TeleportImpl = {
|
|||
}
|
||||
}
|
||||
|
||||
const mountToTarget = () => {
|
||||
const target = (n2.target = resolveTarget(n2.props, querySelector))
|
||||
if (target) {
|
||||
insert(targetStart, target)
|
||||
insert(targetAnchor, target)
|
||||
// #2652 we could be teleporting from a non-SVG tree into an SVG tree
|
||||
if (namespace !== 'svg' && isTargetSVG(target)) {
|
||||
namespace = 'svg'
|
||||
} else if (namespace !== 'mathml' && isTargetMathML(target)) {
|
||||
namespace = 'mathml'
|
||||
}
|
||||
if (!disabled) {
|
||||
mount(target, targetAnchor)
|
||||
updateCssVars(n2)
|
||||
}
|
||||
} else if (__DEV__ && !disabled) {
|
||||
warn(
|
||||
'Invalid Teleport target on mount:',
|
||||
target,
|
||||
`(${typeof target})`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
mount(container, mainAnchor)
|
||||
} else if (target) {
|
||||
mount(target, targetAnchor)
|
||||
updateCssVars(n2)
|
||||
}
|
||||
|
||||
if (isTeleportDeferred(n2.props)) {
|
||||
queuePostRenderEffect(mountToTarget, parentSuspense)
|
||||
} else {
|
||||
mountToTarget()
|
||||
}
|
||||
} else {
|
||||
// update content
|
||||
|
@ -249,9 +270,8 @@ export const TeleportImpl = {
|
|||
)
|
||||
}
|
||||
}
|
||||
updateCssVars(n2)
|
||||
}
|
||||
|
||||
updateCssVars(n2)
|
||||
},
|
||||
|
||||
remove(
|
||||
|
@ -441,7 +461,7 @@ function updateCssVars(vnode: VNode) {
|
|||
// code path here can assume browser environment.
|
||||
const ctx = vnode.ctx
|
||||
if (ctx && ctx.ut) {
|
||||
let node = (vnode.children as VNode[])[0].el!
|
||||
let node = vnode.targetStart
|
||||
while (node && node !== vnode.targetAnchor) {
|
||||
if (node.nodeType === 1) node.setAttribute('data-v-owner', ctx.uid)
|
||||
node = node.nextSibling
|
||||
|
|
|
@ -127,7 +127,9 @@ export function invalidateJob(job: SchedulerJob) {
|
|||
|
||||
export function queuePostFlushCb(cb: SchedulerJobs) {
|
||||
if (!isArray(cb)) {
|
||||
if (!(cb.flags! & SchedulerJobFlags.QUEUED)) {
|
||||
if (activePostFlushCbs && cb.id === -1) {
|
||||
activePostFlushCbs.splice(postFlushIndex + 1, 0, cb)
|
||||
} else if (!(cb.flags! & SchedulerJobFlags.QUEUED)) {
|
||||
pendingPostFlushCbs.push(cb)
|
||||
if (!(cb.flags! & SchedulerJobFlags.ALLOW_RECURSE)) {
|
||||
cb.flags! |= SchedulerJobFlags.QUEUED
|
||||
|
|
Loading…
Reference in New Issue