wip: suspense refactor
This commit is contained in:
parent
c9e625864a
commit
a16c87be63
@ -23,7 +23,6 @@ describe('renderer: suspense', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// TODO test mounted hook & watch callback buffering
|
||||
const AsyncChild = createAsyncComponent(
|
||||
() =>
|
||||
new Promise(resolve => {
|
||||
@ -62,7 +61,6 @@ describe('renderer: suspense', () => {
|
||||
const Comp = {
|
||||
name: 'root',
|
||||
setup() {
|
||||
// TODO test fallback
|
||||
return () =>
|
||||
h(Suspense, [msg.value, h(Mid), h(AsyncChild2, { msg: 'child 2' })])
|
||||
}
|
||||
@ -79,7 +77,9 @@ describe('renderer: suspense', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.todo('fallback content update')
|
||||
test.todo('buffer mounted/updated hooks & watch callbacks')
|
||||
|
||||
test.todo('fallback content')
|
||||
|
||||
test.todo('content update before suspense resolve')
|
||||
|
||||
|
@ -44,7 +44,11 @@ import { pushWarningContext, popWarningContext, warn } from './warning'
|
||||
import { invokeDirectiveHook } from './directives'
|
||||
import { ComponentPublicInstance } from './componentPublicInstanceProxy'
|
||||
import { App, createAppAPI } from './apiApp'
|
||||
import { SuspenseBoundary, createSuspenseBoundary } from './suspense'
|
||||
import {
|
||||
SuspenseBoundary,
|
||||
createSuspenseBoundary,
|
||||
normalizeSuspenseChildren
|
||||
} from './suspense'
|
||||
import { provide } from './apiInject'
|
||||
|
||||
const prodEffectOptions = {
|
||||
@ -609,26 +613,6 @@ export function createRenderer<
|
||||
parentSuspense: SuspenseBoundary<HostNode, HostElement> | null = null
|
||||
) {
|
||||
if (n1 == null) {
|
||||
const contentContainer = hostCreateElement('div')
|
||||
|
||||
function retry() {
|
||||
processFragment(
|
||||
suspense.oldSubTree,
|
||||
suspense.subTree as HostVNode,
|
||||
contentContainer,
|
||||
null,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
optimized
|
||||
)
|
||||
if (suspense.deps > 0) {
|
||||
// still pending.
|
||||
// TODO patch the fallback tree.
|
||||
} else {
|
||||
suspense.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
function resolve() {
|
||||
// unmount fallback tree
|
||||
unmount(suspense.fallbackTree as HostVNode, parentComponent, true)
|
||||
@ -657,7 +641,7 @@ export function createRenderer<
|
||||
const suspense = (n2.suspense = createSuspenseBoundary(
|
||||
n2,
|
||||
parentSuspense,
|
||||
retry,
|
||||
hostCreateElement('div'),
|
||||
resolve
|
||||
))
|
||||
|
||||
@ -668,15 +652,16 @@ export function createRenderer<
|
||||
setCurrentInstance(null)
|
||||
}
|
||||
|
||||
// start mounting the subtree off-dom
|
||||
const { content, fallback } = normalizeSuspenseChildren(n2)
|
||||
suspense.subTree = content
|
||||
suspense.fallbackTree = fallback
|
||||
|
||||
// start mounting the content subtree in an off-dom container
|
||||
// TODO should buffer postQueue jobs on current boundary
|
||||
const subTree = (suspense.subTree = suspense.oldSubTree = childrenToFragment(
|
||||
n2
|
||||
))
|
||||
processFragment(
|
||||
patch(
|
||||
null,
|
||||
subTree as HostVNode,
|
||||
contentContainer,
|
||||
content,
|
||||
suspense.container,
|
||||
null,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
@ -684,14 +669,19 @@ export function createRenderer<
|
||||
)
|
||||
// now check if we have encountered any async deps
|
||||
if (suspense.deps > 0) {
|
||||
// TODO mount the fallback tree.
|
||||
processEmptyNode(
|
||||
// mount the fallback tree
|
||||
patch(
|
||||
null,
|
||||
(suspense.fallbackTree = createVNode(Empty)),
|
||||
fallback,
|
||||
container,
|
||||
anchor
|
||||
anchor,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
optimized
|
||||
)
|
||||
n2.el = fallback.el
|
||||
} else {
|
||||
// Suspense has no async deps. Just resolve.
|
||||
suspense.resolve()
|
||||
}
|
||||
} else {
|
||||
@ -700,34 +690,52 @@ export function createRenderer<
|
||||
HostElement
|
||||
>
|
||||
suspense.vnode = n2
|
||||
const { content, fallback } = normalizeSuspenseChildren(n2)
|
||||
const oldSubTree = (suspense.oldSubTree = suspense.subTree)
|
||||
const newContentTree = (suspense.subTree = childrenToFragment(n2))
|
||||
suspense.subTree = content
|
||||
const oldFallbackTree = (suspense.oldFallbackTree = suspense.fallbackTree)
|
||||
suspense.fallbackTree = fallback
|
||||
if (!suspense.isResolved) {
|
||||
suspense.retry()
|
||||
} else {
|
||||
// just normal patch inner content as a fragment
|
||||
processFragment(
|
||||
patch(
|
||||
oldSubTree,
|
||||
newContentTree,
|
||||
container,
|
||||
content,
|
||||
suspense.container,
|
||||
null,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
optimized
|
||||
)
|
||||
n2.el = newContentTree.el
|
||||
if (suspense.deps > 0) {
|
||||
// still pending. patch the fallback tree.
|
||||
patch(
|
||||
oldFallbackTree,
|
||||
fallback,
|
||||
container,
|
||||
anchor,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
optimized
|
||||
)
|
||||
n2.el = fallback.el
|
||||
} else {
|
||||
suspense.resolve()
|
||||
}
|
||||
} else {
|
||||
// just normal patch inner content as a fragment
|
||||
patch(
|
||||
oldSubTree,
|
||||
content,
|
||||
container,
|
||||
anchor,
|
||||
parentComponent,
|
||||
isSVG,
|
||||
optimized
|
||||
)
|
||||
n2.el = content.el
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function childrenToFragment(vnode: HostVNode): HostVNode {
|
||||
return vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN
|
||||
? createVNode(Fragment, null, vnode.children)
|
||||
: vnode.shapeFlag & ShapeFlags.TEXT_CHILDREN
|
||||
? createVNode(Fragment, null, [vnode.children])
|
||||
: createVNode(Fragment, null, [])
|
||||
}
|
||||
|
||||
function processComponent(
|
||||
n1: HostVNode | null,
|
||||
n2: HostVNode,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { VNode } from './vnode'
|
||||
import { VNode, normalizeVNode } from './vnode'
|
||||
import { ShapeFlags } from '.'
|
||||
|
||||
export const SuspenseSymbol = __DEV__ ? Symbol('Suspense key') : Symbol()
|
||||
|
||||
@ -9,6 +10,7 @@ export interface SuspenseBoundary<
|
||||
> {
|
||||
vnode: HostVNode
|
||||
parent: SuspenseBoundary<HostNode, HostElement> | null
|
||||
container: HostElement
|
||||
subTree: HostVNode | null
|
||||
oldSubTree: HostVNode | null
|
||||
fallbackTree: HostVNode | null
|
||||
@ -16,19 +18,19 @@ export interface SuspenseBoundary<
|
||||
deps: number
|
||||
isResolved: boolean
|
||||
bufferedJobs: Function[]
|
||||
retry(): void
|
||||
resolve(): void
|
||||
}
|
||||
|
||||
export function createSuspenseBoundary<HostNode, HostElement>(
|
||||
vnode: VNode<HostNode, HostElement>,
|
||||
parent: SuspenseBoundary<HostNode, HostElement> | null,
|
||||
retry: () => void,
|
||||
container: HostElement,
|
||||
resolve: () => void
|
||||
): SuspenseBoundary<HostNode, HostElement> {
|
||||
return {
|
||||
vnode,
|
||||
parent,
|
||||
container,
|
||||
deps: 0,
|
||||
subTree: null,
|
||||
oldSubTree: null,
|
||||
@ -36,7 +38,27 @@ export function createSuspenseBoundary<HostNode, HostElement>(
|
||||
oldFallbackTree: null,
|
||||
isResolved: false,
|
||||
bufferedJobs: [],
|
||||
retry,
|
||||
resolve
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeSuspenseChildren(
|
||||
vnode: VNode
|
||||
): {
|
||||
content: VNode
|
||||
fallback: VNode
|
||||
} {
|
||||
const { shapeFlag } = vnode
|
||||
const children = vnode.children as any
|
||||
if (shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
|
||||
return {
|
||||
content: normalizeVNode(children.default()),
|
||||
fallback: normalizeVNode(children.fallback ? children.fallback() : null)
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
content: normalizeVNode(children),
|
||||
fallback: normalizeVNode(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user