]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: suspense refactor
authorEvan You <yyx990803@gmail.com>
Tue, 10 Sep 2019 15:01:11 +0000 (11:01 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 11 Sep 2019 15:10:13 +0000 (11:10 -0400)
packages/runtime-core/__tests__/rendererSuspense.spec.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/suspense.ts

index a577c5e90dedb4fe0f458bffbc76d7c23351e33e..faafcbf3514952ee88958ad17750ac9d48160395 100644 (file)
@@ -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')
 
index 7ea13d9409268419aeeb38f15c0d349ba5ce127a..ff73dcdf480cd157fb65c92524458f5b88b21ddc 100644 (file)
@@ -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()
+        patch(
+          oldSubTree,
+          content,
+          suspense.container,
+          null,
+          parentComponent,
+          isSVG,
+          optimized
+        )
+        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
-        processFragment(
+        patch(
           oldSubTree,
-          newContentTree,
+          content,
           container,
-          null,
+          anchor,
           parentComponent,
           isSVG,
           optimized
         )
-        n2.el = newContentTree.el
+        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,
index f5f2f84bee95252e9665b7c7e7f3dfd7ec3e5e93..16571af46841ebab3d97c7eb1096b0efc8ab5b6f 100644 (file)
@@ -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)
+    }
+  }
+}