]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): fix ssr on-the-fly compilation + slot fallback branch helper injection
authorEvan You <yyx990803@gmail.com>
Fri, 6 Mar 2020 19:52:15 +0000 (14:52 -0500)
committerEvan You <yyx990803@gmail.com>
Fri, 6 Mar 2020 19:52:15 +0000 (14:52 -0500)
packages/compiler-ssr/__tests__/ssrComponent.spec.ts
packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
packages/compiler-ssr/src/transforms/ssrVSlot.ts [deleted file]
packages/runtime-core/src/component.ts
packages/runtime-core/src/components/Suspense.ts
packages/server-renderer/src/renderToString.ts

index 642289c29a642043ee2bde3205d2a66d63ad9315..4414ad3a89110c2db880031c154eaaf536e748da 100644 (file)
@@ -70,7 +70,7 @@ describe('ssr: components', () => {
     test('explicit default slot', () => {
       expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
         .toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
@@ -82,7 +82,7 @@ describe('ssr: components', () => {
                 _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
               } else {
                 return [
-                  _createTextVNode(_toDisplayString(msg + _ctx.outer))
+                  _createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */)
                 ]
               }
             },
@@ -168,7 +168,7 @@ describe('ssr: components', () => {
         <template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
       </foo>`).code
       ).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
@@ -183,7 +183,7 @@ describe('ssr: components', () => {
                     _push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
                   } else {
                     return [
-                      _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar))
+                      _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
                     ]
                   }
                 }
index 5a3ad978ffad8b366891442b4c875d3bb893700e..0e3f1457bf4c6cd40e5a9fc1cc9bb5f01c86b8e7 100644 (file)
@@ -80,6 +80,9 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
   const clonedNode = clone(node)
 
   return function ssrPostTransformComponent() {
+    // Using the cloned node, build the normal VNode-based branches (for
+    // fallback in case the child is render-fn based). Store them in an array
+    // for later use.
     buildSlots(clonedNode, context, (props, children) => {
       vnodeBranches.push(createVNodeSlotBranch(props, children, context))
       return createFunctionExpression(undefined)
@@ -106,9 +109,7 @@ export const ssrTransformComponent: NodeTransform = (node, context) => {
       wipEntries.push({
         fn,
         children,
-        // build the children using normal vnode-based transforms
-        // TODO fixme: `children` here has already been mutated at this point
-        // so the sub-transform runs into errors :/
+        // also collect the corresponding vnode branch built earlier
         vnodeBranch: vnodeBranches[wipEntries.length]
       })
       return fn
@@ -266,6 +267,9 @@ function subTransform(
 ) {
   const childRoot = createRoot([node])
   const childContext = createTransformContext(childRoot, options)
+  // this sub transform is for vnode fallback branch so it should be handled
+  // like normal render functions
+  childContext.ssr = false
   // inherit parent scope analysis state
   childContext.scopes = { ...parentContext.scopes }
   childContext.identifiers = { ...parentContext.identifiers }
diff --git a/packages/compiler-ssr/src/transforms/ssrVSlot.ts b/packages/compiler-ssr/src/transforms/ssrVSlot.ts
deleted file mode 100644 (file)
index 70b786d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-// TODO
index 29ea5151eb0fd98ab47050f0fb3fcd1bb2ff02c1..ad8453e9c0f30d47169d288a27c046a34f84e800 100644 (file)
@@ -300,7 +300,7 @@ export function setupComponent(
   // setup stateful logic
   let setupResult
   if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
-    setupResult = setupStatefulComponent(instance, parentSuspense)
+    setupResult = setupStatefulComponent(instance, parentSuspense, isSSR)
   }
   isInSSRComponentSetup = false
   return setupResult
@@ -308,7 +308,8 @@ export function setupComponent(
 
 function setupStatefulComponent(
   instance: ComponentInternalInstance,
-  parentSuspense: SuspenseBoundary | null
+  parentSuspense: SuspenseBoundary | null,
+  isSSR: boolean
 ) {
   const Component = instance.type as ComponentOptions
 
@@ -362,7 +363,7 @@ function setupStatefulComponent(
       if (isInSSRComponentSetup) {
         // return the promise so server-renderer can wait on it
         return setupResult.then(resolvedResult => {
-          handleSetupResult(instance, resolvedResult, parentSuspense)
+          handleSetupResult(instance, resolvedResult, parentSuspense, isSSR)
         })
       } else if (__FEATURE_SUSPENSE__) {
         // async setup returned Promise.
@@ -375,17 +376,18 @@ function setupStatefulComponent(
         )
       }
     } else {
-      handleSetupResult(instance, setupResult, parentSuspense)
+      handleSetupResult(instance, setupResult, parentSuspense, isSSR)
     }
   } else {
-    finishComponentSetup(instance, parentSuspense)
+    finishComponentSetup(instance, parentSuspense, isSSR)
   }
 }
 
 export function handleSetupResult(
   instance: ComponentInternalInstance,
   setupResult: unknown,
-  parentSuspense: SuspenseBoundary | null
+  parentSuspense: SuspenseBoundary | null,
+  isSSR: boolean
 ) {
   if (isFunction(setupResult)) {
     // setup returned an inline render function
@@ -407,7 +409,7 @@ export function handleSetupResult(
       }`
     )
   }
-  finishComponentSetup(instance, parentSuspense)
+  finishComponentSetup(instance, parentSuspense, isSSR)
 }
 
 type CompileFunction = (
@@ -424,10 +426,17 @@ export function registerRuntimeCompiler(_compile: any) {
 
 function finishComponentSetup(
   instance: ComponentInternalInstance,
-  parentSuspense: SuspenseBoundary | null
+  parentSuspense: SuspenseBoundary | null,
+  isSSR: boolean
 ) {
   const Component = instance.type as ComponentOptions
-  if (!instance.render) {
+
+  // template / render function normalization
+  if (__NODE_JS__ && isSSR) {
+    if (Component.render) {
+      instance.render = Component.render as RenderFunction
+    }
+  } else if (!instance.render) {
     if (__RUNTIME_COMPILE__ && Component.template && !Component.render) {
       // __RUNTIME_COMPILE__ ensures `compile` is provided
       Component.render = compile!(Component.template, {
@@ -437,7 +446,7 @@ function finishComponentSetup(
       ;(Component.render as RenderFunction).isRuntimeCompiled = true
     }
 
-    if (__DEV__ && !Component.render && !Component.ssrRender) {
+    if (__DEV__ && !Component.render) {
       /* istanbul ignore if */
       if (!__RUNTIME_COMPILE__ && Component.template) {
         warn(
index d27c1f29f58e3eb6f81966b6b294cb0759e4ab46..b3a6b31c6d4387670c0bb3c5e4febab11bb0cb4b 100644 (file)
@@ -410,7 +410,7 @@ function createSuspenseBoundary<HostNode, HostElement>(
           if (__DEV__) {
             pushWarningContext(vnode)
           }
-          handleSetupResult(instance, asyncSetupResult, suspense)
+          handleSetupResult(instance, asyncSetupResult, suspense, false)
           // unset placeholder, otherwise this will be treated as a hydration mount
           vnode.el = null
           setupRenderEffect(
index 5b5ecc4d57a45c77146dd6ce0a2d25255281f4c0..b181b5422cb31b65c84449e73f2d86f1e24aa212 100644 (file)
@@ -155,6 +155,37 @@ function renderComponentVNode(
   }
 }
 
+function renderComponentSubTree(
+  instance: ComponentInternalInstance
+): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
+  const comp = instance.type as Component
+  const { getBuffer, push } = createBuffer()
+  if (isFunction(comp)) {
+    renderVNode(push, renderComponentRoot(instance), instance)
+  } else {
+    if (!instance.render && !comp.ssrRender && isString(comp.template)) {
+      comp.ssrRender = ssrCompile(comp.template, instance)
+    }
+
+    if (comp.ssrRender) {
+      // optimized
+      // set current rendering instance for asset resolution
+      setCurrentRenderingInstance(instance)
+      comp.ssrRender(instance.proxy, push, instance)
+      setCurrentRenderingInstance(null)
+    } else if (instance.render) {
+      renderVNode(push, renderComponentRoot(instance), instance)
+    } else {
+      throw new Error(
+        `Component ${
+          comp.name ? `${comp.name} ` : ``
+        } is missing template or render function.`
+      )
+    }
+  }
+  return getBuffer()
+}
+
 type SSRRenderFunction = (
   context: any,
   push: (item: any) => void,
@@ -190,38 +221,7 @@ function ssrCompile(
       }
     }
   })
-  return (compileCache[template] = Function(code)())
-}
-
-function renderComponentSubTree(
-  instance: ComponentInternalInstance
-): ResolvedSSRBuffer | Promise<ResolvedSSRBuffer> {
-  const comp = instance.type as Component
-  const { getBuffer, push } = createBuffer()
-  if (isFunction(comp)) {
-    renderVNode(push, renderComponentRoot(instance), instance)
-  } else {
-    if (!instance.render && !comp.ssrRender && isString(comp.template)) {
-      comp.ssrRender = ssrCompile(comp.template, instance)
-    }
-
-    if (comp.ssrRender) {
-      // optimized
-      // set current rendering instance for asset resolution
-      setCurrentRenderingInstance(instance)
-      comp.ssrRender(instance.proxy, push, instance)
-      setCurrentRenderingInstance(null)
-    } else if (instance.render) {
-      renderVNode(push, renderComponentRoot(instance), instance)
-    } else {
-      throw new Error(
-        `Component ${
-          comp.name ? `${comp.name} ` : ``
-        } is missing template or render function.`
-      )
-    }
-  }
-  return getBuffer()
+  return (compileCache[template] = Function('require', code)(require))
 }
 
 function renderVNode(