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