]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(ssr): renderer support for optimized and manual slots
authorEvan You <yyx990803@gmail.com>
Wed, 29 Jan 2020 03:58:02 +0000 (22:58 -0500)
committerEvan You <yyx990803@gmail.com>
Wed, 29 Jan 2020 03:58:02 +0000 (22:58 -0500)
packages/runtime-core/__tests__/rendererPortal.spec.ts
packages/runtime-core/src/componentSlots.ts
packages/runtime-core/src/components/BaseTransition.ts
packages/runtime-core/src/h.ts
packages/runtime-core/src/helpers/renderSlot.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/renderer.ts
packages/runtime-core/src/vnode.ts
packages/server-renderer/src/renderToString.ts

index e5146c49467cbcc107c5207b77330c22ca0f1788..c7d600bfca03ac293390ea54a4a06d461728600b 100644 (file)
@@ -12,7 +12,7 @@ import {
   TestElement,
   TestNode
 } from '@vue/runtime-test'
-import { VNodeChildren } from '../src/vnode'
+import { VNodeArrayChildren } from '../src/vnode'
 
 describe('renderer: portal', () => {
   test('should work', () => {
@@ -60,7 +60,7 @@ describe('renderer: portal', () => {
   test('should update children', async () => {
     const target = nodeOps.createElement('div')
     const root = nodeOps.createElement('div')
-    const children = ref<VNodeChildren<TestNode, TestElement>>([
+    const children = ref<VNodeArrayChildren<TestNode, TestElement>>([
       h('div', 'teleported')
     ])
 
index 583e12db645ff21264102ff3150f9c9d9d457fbe..ccac9f938f95bffb804561f5cf04aee1f8bff97a 100644 (file)
@@ -1,5 +1,10 @@
 import { ComponentInternalInstance, currentInstance } from './component'
-import { VNode, NormalizedChildren, normalizeVNode, VNodeChild } from './vnode'
+import {
+  VNode,
+  VNodeNormalizedChildren,
+  normalizeVNode,
+  VNodeChild
+} from './vnode'
 import { isArray, isFunction, EMPTY_OBJ } from '@vue/shared'
 import { ShapeFlags } from './shapeFlags'
 import { warn } from './warning'
@@ -41,7 +46,7 @@ const normalizeSlot = (key: string, rawSlot: Function): Slot => (
 
 export function resolveSlots(
   instance: ComponentInternalInstance,
-  children: NormalizedChildren
+  children: VNodeNormalizedChildren
 ) {
   let slots: InternalSlots | void
   if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
index 7fa736aea5540bd1beaf0b1c7826a7267ba00c85..bcc2a4bde02b9939a8b6390b2512777486f5019d 100644 (file)
@@ -9,7 +9,7 @@ import {
   Comment,
   isSameVNodeType,
   VNode,
-  VNodeChildren
+  VNodeArrayChildren
 } from '../vnode'
 import { warn } from '../warning'
 import { isKeepAlive } from './KeepAlive'
@@ -370,7 +370,7 @@ function emptyPlaceholder(vnode: VNode): VNode | undefined {
 function getKeepAliveChild(vnode: VNode): VNode | undefined {
   return isKeepAlive(vnode)
     ? vnode.children
-      ? ((vnode.children as VNodeChildren)[0] as VNode)
+      ? ((vnode.children as VNodeArrayChildren)[0] as VNode)
       : undefined
     : vnode
 }
index 2137be2df1a85dcdad2e5682684d16d14e8b3aa8..fae16b8502f8d357399e6fc418e60e2b7078c971 100644 (file)
@@ -2,7 +2,7 @@ import {
   VNode,
   VNodeProps,
   createVNode,
-  VNodeChildren,
+  VNodeArrayChildren,
   Fragment,
   Portal,
   isVNode
@@ -62,7 +62,7 @@ type RawChildren =
   | number
   | boolean
   | VNode
-  | VNodeChildren
+  | VNodeArrayChildren
   | (() => any)
 
 // fake constructor type returned from `defineComponent`
@@ -85,11 +85,11 @@ export function h(
 ): VNode
 
 // fragment
-export function h(type: typeof Fragment, children?: VNodeChildren): VNode
+export function h(type: typeof Fragment, children?: VNodeArrayChildren): VNode
 export function h(
   type: typeof Fragment,
   props?: RawProps | null,
-  children?: VNodeChildren
+  children?: VNodeArrayChildren
 ): VNode
 
 // portal (target prop is required)
index 7c1635d2a47751bcd141671e967aa22a0d8213a3..3200dd3d10bcc61c46b3014b63d34c42a0f5db38 100644 (file)
@@ -1,7 +1,7 @@
 import { Data } from '../component'
 import { Slot } from '../componentSlots'
 import {
-  VNodeChildren,
+  VNodeArrayChildren,
   openBlock,
   createBlock,
   Fragment,
@@ -15,7 +15,7 @@ export function renderSlot(
   props: Data = {},
   // this is not a user-facing function, so the fallback is always generated by
   // the compiler and guaranteed to be an array
-  fallback?: VNodeChildren
+  fallback?: VNodeArrayChildren
 ): VNode {
   const slot = slots[name]
   return (
index df3d04bd5375dd5654ea5c8140020a48f2af19ba..9d82189359d779a14f9398ce17d060ba8c0d9d6b 100644 (file)
@@ -125,7 +125,13 @@ export {
   Plugin,
   CreateAppFunction
 } from './apiCreateApp'
-export { VNode, VNodeTypes, VNodeProps, VNodeChildren } from './vnode'
+export {
+  VNode,
+  VNodeTypes,
+  VNodeProps,
+  VNodeArrayChildren,
+  VNodeNormalizedChildren
+} from './vnode'
 export {
   Component,
   FunctionalComponent,
index d545d75ed4d8af021ce9d30c58424d1f3fd421df..f4c68387f63913d9f3b432bb6798cd01b55d4c57 100644 (file)
@@ -6,7 +6,7 @@ import {
   cloneIfMounted,
   normalizeVNode,
   VNode,
-  VNodeChildren,
+  VNodeArrayChildren,
   createVNode,
   isSameVNodeType
 } from './vnode'
@@ -177,7 +177,7 @@ export function createRenderer<
   createApp: CreateAppFunction<HostElement>
 } {
   type HostVNode = VNode<HostNode, HostElement>
-  type HostVNodeChildren = VNodeChildren<HostNode, HostElement>
+  type HostVNodeChildren = VNodeArrayChildren<HostNode, HostElement>
   type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
 
   const {
index c23ddd80106ace1cdd78b035e1cc807401b54fce..2552f6c0d7e834697359fba5642b72a8ac930041 100644 (file)
@@ -71,19 +71,19 @@ type VNodeChildAtom<HostNode, HostElement> =
   | null
   | void
 
-export interface VNodeChildren<HostNode = any, HostElement = any>
+export interface VNodeArrayChildren<HostNode = any, HostElement = any>
   extends Array<
-      | VNodeChildren<HostNode, HostElement>
+      | VNodeArrayChildren<HostNode, HostElement>
       | VNodeChildAtom<HostNode, HostElement>
     > {}
 
 export type VNodeChild<HostNode = any, HostElement = any> =
   | VNodeChildAtom<HostNode, HostElement>
-  | VNodeChildren<HostNode, HostElement>
+  | VNodeArrayChildren<HostNode, HostElement>
 
-export type NormalizedChildren<HostNode = any, HostElement = any> =
+export type VNodeNormalizedChildren<HostNode = any, HostElement = any> =
   | string
-  | VNodeChildren<HostNode, HostElement>
+  | VNodeArrayChildren<HostNode, HostElement>
   | RawSlots
   | null
 
@@ -94,7 +94,7 @@ export interface VNode<HostNode = any, HostElement = any> {
   key: string | number | null
   ref: string | Ref | ((ref: object | null) => void) | null
   scopeId: string | null // SFC only
-  children: NormalizedChildren<HostNode, HostElement>
+  children: VNodeNormalizedChildren<HostNode, HostElement>
   component: ComponentInternalInstance | null
   suspense: SuspenseBoundary<HostNode, HostElement> | null
   dirs: DirectiveBinding[] | null
@@ -376,7 +376,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) {
     children = String(children)
     type = ShapeFlags.TEXT_CHILDREN
   }
-  vnode.children = children as NormalizedChildren
+  vnode.children = children as VNodeNormalizedChildren
   vnode.shapeFlag |= type
 }
 
index 412fdb277900ecf88cb55725f58fe1a932deca4a..ff3e2749cc1339fb73306e56639597f988769da1 100644 (file)
@@ -3,21 +3,24 @@ import {
   Component,
   ComponentInternalInstance,
   VNode,
-  VNodeChildren,
+  VNodeArrayChildren,
+  VNodeNormalizedChildren,
   createVNode,
   Text,
   Comment,
   Fragment,
   Portal,
   ShapeFlags,
-  ssrUtils
+  ssrUtils,
+  Slot
 } from 'vue'
 import {
   isString,
   isPromise,
   isArray,
   isFunction,
-  isVoidTag
+  isVoidTag,
+  EMPTY_OBJ
 } from '@vue/shared'
 import { renderProps } from './renderProps'
 import { escape } from './escape'
@@ -39,6 +42,7 @@ type SSRBuffer = SSRBufferItem[]
 type SSRBufferItem = string | ResolvedSSRBuffer | Promise<ResolvedSSRBuffer>
 type ResolvedSSRBuffer = (string | ResolvedSSRBuffer)[]
 type PushFn = (item: SSRBufferItem) => void
+type Props = Record<string, unknown>
 
 function createBuffer() {
   let appendable = false
@@ -79,26 +83,36 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
 }
 
 export async function renderToString(app: App): Promise<string> {
-  const resolvedBuffer = await renderComponent(
-    createVNode(app._component, app._props)
-  )
+  const resolvedBuffer = await renderComponent(app._component, app._props, null)
   return unrollBuffer(resolvedBuffer)
 }
 
 export function renderComponent(
+  comp: Component,
+  props: Props | null,
+  children: VNodeNormalizedChildren | null,
+  parentComponent: ComponentInternalInstance | null = null
+): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
+  return renderComponentVNode(
+    createVNode(comp, props, children),
+    parentComponent
+  )
+}
+
+function renderComponentVNode(
   vnode: VNode,
   parentComponent: ComponentInternalInstance | null = null
 ): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
   const instance = createComponentInstance(vnode, parentComponent)
   const res = setupComponent(instance, null)
   if (isPromise(res)) {
-    return res.then(() => innerRenderComponent(instance))
+    return res.then(() => renderComponentSubTree(instance))
   } else {
-    return innerRenderComponent(instance)
+    return renderComponentSubTree(instance)
   }
 }
 
-function innerRenderComponent(
+function renderComponentSubTree(
   instance: ComponentInternalInstance
 ): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
   const comp = instance.type as Component
@@ -141,7 +155,7 @@ export function renderVNode(
       break
     case Fragment:
       push(`<!---->`)
-      renderVNodeChildren(push, children as VNodeChildren, parentComponent)
+      renderVNodeChildren(push, children as VNodeArrayChildren, parentComponent)
       push(`<!---->`)
       break
     case Portal:
@@ -151,7 +165,7 @@ export function renderVNode(
       if (shapeFlag & ShapeFlags.ELEMENT) {
         renderElement(push, vnode, parentComponent)
       } else if (shapeFlag & ShapeFlags.COMPONENT) {
-        push(renderComponent(vnode, parentComponent))
+        push(renderComponentVNode(vnode, parentComponent))
       } else if (shapeFlag & ShapeFlags.SUSPENSE) {
         // TODO
       } else {
@@ -166,7 +180,7 @@ export function renderVNode(
 
 function renderVNodeChildren(
   push: PushFn,
-  children: VNodeChildren,
+  children: VNodeArrayChildren,
   parentComponent: ComponentInternalInstance | null = null
 ) {
   for (let i = 0; i < children.length; i++) {
@@ -218,13 +232,37 @@ function renderElement(
       if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
         push(escape(children as string))
       } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
-        renderVNodeChildren(push, children as VNodeChildren, parentComponent)
+        renderVNodeChildren(
+          push,
+          children as VNodeArrayChildren,
+          parentComponent
+        )
       }
     }
     push(`</${tag}>`)
   }
 }
 
-export function renderSlot() {
-  // TODO
+type OptimizedSlotFn = (
+  props: Props,
+  push: PushFn,
+  parentComponent: ComponentInternalInstance | null
+) => void
+
+export function renderSlot(
+  slotFn: Slot | OptimizedSlotFn,
+  slotProps: Props,
+  push: PushFn,
+  parentComponent: ComponentInternalInstance | null = null
+) {
+  // template-compiled slots are always rendered as fragments
+  push(`<!---->`)
+  if (slotFn.length > 2) {
+    // only ssr-optimized slot fns accept 3 arguments
+    slotFn(slotProps, push, parentComponent)
+  } else {
+    // normal slot
+    renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
+  }
+  push(`<!---->`)
 }