parent
f0eb1978b2
commit
9f24195d2c
@ -23,6 +23,7 @@ import {
|
|||||||
createApp
|
createApp
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { PatchFlags, SlotFlags } from '@vue/shared'
|
import { PatchFlags, SlotFlags } from '@vue/shared'
|
||||||
|
import { SuspenseImpl } from '../src/components/Suspense'
|
||||||
|
|
||||||
describe('renderer: optimized mode', () => {
|
describe('renderer: optimized mode', () => {
|
||||||
let root: TestElement
|
let root: TestElement
|
||||||
@ -784,4 +785,40 @@ describe('renderer: optimized mode', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(inner(root)).toBe('<div><div><span>loading</span></div></div>')
|
expect(inner(root)).toBe('<div><div><span>loading</span></div></div>')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #3828
|
||||||
|
test('patch Suspense in optimized mode w/ nested dynamic nodes', async () => {
|
||||||
|
const show = ref(false)
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
openBlock(),
|
||||||
|
createBlock(
|
||||||
|
Fragment,
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
(openBlock(),
|
||||||
|
createBlock(SuspenseImpl, null, {
|
||||||
|
default: withCtx(() => [
|
||||||
|
createVNode('div', null, [
|
||||||
|
createVNode('div', null, show.value, PatchFlags.TEXT)
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
_: SlotFlags.STABLE
|
||||||
|
}))
|
||||||
|
],
|
||||||
|
PatchFlags.STABLE_FRAGMENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.mount(root)
|
||||||
|
expect(inner(root)).toBe('<div><div>false</div></div>')
|
||||||
|
|
||||||
|
show.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(inner(root)).toBe('<div><div>true</div></div>')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
VNode,
|
VNode,
|
||||||
normalizeVNode,
|
normalizeVNode,
|
||||||
VNodeChild,
|
|
||||||
VNodeProps,
|
VNodeProps,
|
||||||
isSameVNodeType
|
isSameVNodeType,
|
||||||
|
openBlock,
|
||||||
|
closeBlock,
|
||||||
|
currentBlock,
|
||||||
|
createVNode
|
||||||
} from '../vnode'
|
} from '../vnode'
|
||||||
import { isFunction, isArray, ShapeFlags, toNumber } from '@vue/shared'
|
import { isFunction, isArray, ShapeFlags, toNumber } from '@vue/shared'
|
||||||
import { ComponentInternalInstance, handleSetupResult } from '../component'
|
import { ComponentInternalInstance, handleSetupResult } from '../component'
|
||||||
@ -79,7 +82,8 @@ export const SuspenseImpl = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
hydrate: hydrateSuspense,
|
hydrate: hydrateSuspense,
|
||||||
create: createSuspenseBoundary
|
create: createSuspenseBoundary,
|
||||||
|
normalize: normalizeSuspenseChildren
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force-casted public typing for h and TSX props inference
|
// Force-casted public typing for h and TSX props inference
|
||||||
@ -709,31 +713,34 @@ function hydrateSuspense(
|
|||||||
/* eslint-enable no-restricted-globals */
|
/* eslint-enable no-restricted-globals */
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeSuspenseChildren(
|
function normalizeSuspenseChildren(vnode: VNode) {
|
||||||
vnode: VNode
|
|
||||||
): {
|
|
||||||
content: VNode
|
|
||||||
fallback: VNode
|
|
||||||
} {
|
|
||||||
const { shapeFlag, children } = vnode
|
const { shapeFlag, children } = vnode
|
||||||
let content: VNode
|
const isSlotChildren = shapeFlag & ShapeFlags.SLOTS_CHILDREN
|
||||||
let fallback: VNode
|
vnode.ssContent = normalizeSuspenseSlot(
|
||||||
if (shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
isSlotChildren ? (children as Slots).default : children
|
||||||
content = normalizeSuspenseSlot((children as Slots).default)
|
)
|
||||||
fallback = normalizeSuspenseSlot((children as Slots).fallback)
|
vnode.ssFallback = isSlotChildren
|
||||||
} else {
|
? normalizeSuspenseSlot((children as Slots).fallback)
|
||||||
content = normalizeSuspenseSlot(children as VNodeChild)
|
: createVNode(Comment)
|
||||||
fallback = normalizeVNode(null)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
content,
|
|
||||||
fallback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeSuspenseSlot(s: any) {
|
function normalizeSuspenseSlot(s: any) {
|
||||||
|
let block: VNode[] | null | undefined
|
||||||
if (isFunction(s)) {
|
if (isFunction(s)) {
|
||||||
|
const isCompiledSlot = s._c
|
||||||
|
if (isCompiledSlot) {
|
||||||
|
// disableTracking: false
|
||||||
|
// allow block tracking for compiled slots
|
||||||
|
// (see ./componentRenderContext.ts)
|
||||||
|
s._d = false
|
||||||
|
openBlock()
|
||||||
|
}
|
||||||
s = s()
|
s = s()
|
||||||
|
if (isCompiledSlot) {
|
||||||
|
s._d = true
|
||||||
|
block = currentBlock
|
||||||
|
closeBlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isArray(s)) {
|
if (isArray(s)) {
|
||||||
const singleChild = filterSingleRoot(s)
|
const singleChild = filterSingleRoot(s)
|
||||||
@ -742,7 +749,11 @@ function normalizeSuspenseSlot(s: any) {
|
|||||||
}
|
}
|
||||||
s = singleChild
|
s = singleChild
|
||||||
}
|
}
|
||||||
return normalizeVNode(s)
|
s = normalizeVNode(s)
|
||||||
|
if (block) {
|
||||||
|
s.dynamicChildren = block.filter(c => c !== s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
export function queueEffectWithSuspense(
|
export function queueEffectWithSuspense(
|
||||||
|
@ -26,8 +26,7 @@ import { AppContext } from './apiCreateApp'
|
|||||||
import {
|
import {
|
||||||
SuspenseImpl,
|
SuspenseImpl,
|
||||||
isSuspense,
|
isSuspense,
|
||||||
SuspenseBoundary,
|
SuspenseBoundary
|
||||||
normalizeSuspenseChildren
|
|
||||||
} from './components/Suspense'
|
} from './components/Suspense'
|
||||||
import { DirectiveBinding } from './directives'
|
import { DirectiveBinding } from './directives'
|
||||||
import { TransitionHooks } from './components/BaseTransition'
|
import { TransitionHooks } from './components/BaseTransition'
|
||||||
@ -186,7 +185,7 @@ export interface VNode<
|
|||||||
// structure would be stable. This allows us to skip most children diffing
|
// structure would be stable. This allows us to skip most children diffing
|
||||||
// and only worry about the dynamic nodes (indicated by patch flags).
|
// and only worry about the dynamic nodes (indicated by patch flags).
|
||||||
export const blockStack: (VNode[] | null)[] = []
|
export const blockStack: (VNode[] | null)[] = []
|
||||||
let currentBlock: VNode[] | null = null
|
export let currentBlock: VNode[] | null = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a block.
|
* Open a block.
|
||||||
@ -452,9 +451,7 @@ function _createVNode(
|
|||||||
|
|
||||||
// normalize suspense children
|
// normalize suspense children
|
||||||
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
|
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
|
||||||
const { content, fallback } = normalizeSuspenseChildren(vnode)
|
;(type as typeof SuspenseImpl).normalize(vnode)
|
||||||
vnode.ssContent = content
|
|
||||||
vnode.ssFallback = fallback
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user