]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: remove unnecessary functional wrapper + delay for async components
authorEvan You <yyx990803@gmail.com>
Wed, 26 Sep 2018 02:25:18 +0000 (22:25 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 26 Sep 2018 02:25:18 +0000 (22:25 -0400)
packages/core/src/componentOptions.ts
packages/core/src/createRenderer.ts
packages/core/src/optional/asyncComponent.ts
packages/core/src/vdom.ts

index d12153a84f9ea35f05ef13a7f76132913ac5875a..fbe25083935032028ded0efd4ad39cc09049476f 100644 (file)
@@ -1,10 +1,10 @@
-import { Slots, VNodeData } from './vdom'
+import { Slots } from './vdom'
 import { MountedComponent } from './component'
 
 export type Data = Record<string, any>
 
 export interface RenderFunction<P = Data> {
-  (props: P, slots: Slots, attrs: Data, rawData: VNodeData | null): any
+  (props: P, slots: Slots, attrs: Data): any
 }
 
 export interface ComponentOptions<D = Data, P = Data> {
index c129777b89d9edf83de15f76d31d665cf5e8a747..6e8a09a8c84892fad0b26df9cefa8a6768556096 100644 (file)
@@ -284,7 +284,7 @@ export function createRenderer(options: RendererOptions) {
       const render = tag as FunctionalComponent
       const { props, attrs } = resolveProps(data, render.props, render)
       const subTree = (vnode.children = normalizeComponentRoot(
-        render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ, data),
+        render(props, slots || EMPTY_OBJ, attrs || EMPTY_OBJ),
         vnode,
         attrs,
         render.inheritAttrs
@@ -581,7 +581,7 @@ export function createRenderer(options: RendererOptions) {
     if (shouldUpdate) {
       const { props, attrs } = resolveProps(nextData, render.props, render)
       const nextTree = (nextVNode.children = normalizeComponentRoot(
-        render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ, nextData),
+        render(props, nextSlots || EMPTY_OBJ, attrs || EMPTY_OBJ),
         nextVNode,
         attrs,
         render.inheritAttrs
@@ -1167,6 +1167,10 @@ export function createRenderer(options: RendererOptions) {
       (__COMPAT__ && (parentVNode.children as MountedComponent)) ||
       createComponentInstance(parentVNode, Component, parentComponent)
 
+    if (instance.beforeMount) {
+      instance.beforeMount.call(instance.$proxy)
+    }
+
     const queueUpdate = (instance.$forceUpdate = () => {
       queueJob(instance._updateHandle, flushHooks)
     })
@@ -1206,9 +1210,6 @@ export function createRenderer(options: RendererOptions) {
     instance: MountedComponent,
     ref: Ref | null
   ) {
-    if (instance.beforeMount) {
-      instance.beforeMount.call(instance.$proxy)
-    }
     if (ref) {
       mountRef(ref, instance)
     }
index 42a7ec22a577d2756f5200e2fab743bf626e0b5f..752035a3a7d02e062debd1bc87f74a9f9a6fb2ce 100644 (file)
@@ -1,6 +1,6 @@
 import { ChildrenFlags } from '../flags'
-import { createComponentVNode, VNodeData } from '../vdom'
-import { Component, ComponentType, FunctionalComponent } from '../component'
+import { createComponentVNode, Slots } from '../vdom'
+import { Component, ComponentType, ComponentClass } from '../component'
 
 export interface AsyncComponentFactory {
   (): Promise<ComponentType>
@@ -22,92 +22,90 @@ export type AsyncComponentOptions =
 interface AsyncContainerData {
   comp: ComponentType | null
   err: Error | null
+  delayed: boolean
   timedOut: boolean
 }
 
-interface AsyncContainerProps {
-  options: AsyncComponentFullOptions
-  rawData: VNodeData | null
-}
-
-export class AsyncContainer extends Component<
-  AsyncContainerData,
-  AsyncContainerProps
-> {
-  data() {
-    return {
-      comp: null,
-      err: null,
-      timedOut: false
-    }
+export function createAsyncComponent(
+  options: AsyncComponentOptions
+): ComponentClass {
+  if (typeof options === 'function') {
+    options = { factory: options }
   }
 
-  created() {
-    const { factory, timeout } = this.$props.options
-    if (factory.resolved) {
-      this.comp = factory.resolved
-    } else {
-      factory()
-        .then(resolved => {
-          this.comp = factory.resolved = resolved
-        })
-        .catch(err => {
-          this.err = err
-        })
-    }
-    if (timeout != null) {
-      setTimeout(() => {
-        this.timedOut = true
-      }, timeout)
+  const {
+    factory,
+    timeout,
+    delay = 200,
+    loading: loadingComp,
+    error: errorComp
+  } = options
+
+  return class AsyncContainer extends Component<AsyncContainerData> {
+    data() {
+      return {
+        comp: null,
+        err: null,
+        delayed: false,
+        timedOut: false
+      }
     }
-  }
 
-  render(props: AsyncContainerProps) {
-    if (this.err || (this.timedOut && !this.comp)) {
-      const error =
-        this.err ||
-        new Error(`Async component timed out after ${props.options.timeout}ms.`)
-      const errorComp = props.options.error
-      return errorComp
-        ? createComponentVNode(
-            errorComp,
-            { error },
-            null,
-            ChildrenFlags.NO_CHILDREN
-          )
-        : null
-    } else if (this.comp) {
-      return createComponentVNode(
-        this.comp,
-        props.rawData,
-        null,
-        ChildrenFlags.UNKNOWN_CHILDREN
-      )
-    } else {
-      const loadingComp = props.options.loading
-      return loadingComp
-        ? createComponentVNode(
-            loadingComp,
-            null,
-            null,
-            ChildrenFlags.NO_CHILDREN
-          )
-        : null
+    // doing this in beforeMount so this is non-SSR only
+    beforeMount() {
+      if (factory.resolved) {
+        this.comp = factory.resolved
+      } else {
+        factory()
+          .then(resolved => {
+            this.comp = factory.resolved = resolved
+          })
+          .catch(err => {
+            this.err = err
+          })
+      }
+      if (timeout != null) {
+        setTimeout(() => {
+          this.timedOut = true
+        }, timeout)
+      }
+      if (delay != null) {
+        this.delayed = true
+        setTimeout(() => {
+          this.delayed = false
+        }, delay)
+      }
     }
-  }
-}
 
-export function createAsyncComponent(
-  options: AsyncComponentOptions
-): FunctionalComponent {
-  if (typeof options === 'function') {
-    options = { factory: options }
-  }
-  return (_, __, ___, rawData) =>
-    createComponentVNode(
-      AsyncContainer,
-      { options, rawData },
-      null,
-      ChildrenFlags.NO_CHILDREN
-    )
+    render(props: any, slots: Slots) {
+      if (this.err || (this.timedOut && !this.comp)) {
+        const error =
+          this.err || new Error(`Async component timed out after ${timeout}ms.`)
+        return errorComp
+          ? createComponentVNode(
+              errorComp,
+              { error },
+              null,
+              ChildrenFlags.NO_CHILDREN
+            )
+          : null
+      } else if (this.comp) {
+        return createComponentVNode(
+          this.comp,
+          props,
+          slots,
+          ChildrenFlags.STABLE_SLOTS
+        )
+      } else {
+        return loadingComp && !this.delayed
+          ? createComponentVNode(
+              loadingComp,
+              null,
+              null,
+              ChildrenFlags.NO_CHILDREN
+            )
+          : null
+      }
+    }
+  } as ComponentClass
 }
index 72a71c451b77624538aaaaf3fb14b2dda0f067a7..578cff95de546c6c54897be08f102fef149989b9 100644 (file)
@@ -123,7 +123,7 @@ export function createElementVNode(
 export function createComponentVNode(
   comp: any,
   data: VNodeData | null,
-  children: VNodeChildren,
+  children: VNodeChildren | Slots,
   childFlags: ChildrenFlags,
   key?: Key | null,
   ref?: Ref | null
@@ -169,7 +169,9 @@ export function createComponentVNode(
 
   // slots
   let slots: any
-  if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
+  if (childFlags === ChildrenFlags.STABLE_SLOTS) {
+    slots = children
+  } else if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
     childFlags = children
       ? ChildrenFlags.DYNAMIC_SLOTS
       : ChildrenFlags.NO_CHILDREN
@@ -361,9 +363,12 @@ export function normalizeVNodes(
 
 // ensure all slot functions return Arrays
 function normalizeSlots(slots: { [name: string]: any }): Slots {
-  const normalized: Slots = {}
+  if (slots._normalized) {
+    return slots
+  }
+  const normalized = { _normalized: true } as any
   for (const name in slots) {
-    normalized[name] = (...args) => normalizeSlot(slots[name](...args))
+    normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
   }
   return normalized
 }