ソースを参照

wip(ssr): render real components

Evan You 6 年 前
コミット
cee36ad028

+ 1 - 0
jest.config.js

@@ -7,6 +7,7 @@ module.exports = {
     __BROWSER__: false,
     __BUNDLER__: true,
     __RUNTIME_COMPILE__: true,
+    __SSR__: false,
     __FEATURE_OPTIONS__: true,
     __FEATURE_SUSPENSE__: true
   },

+ 1 - 0
packages/global.d.ts

@@ -4,6 +4,7 @@ declare var __TEST__: boolean
 declare var __BROWSER__: boolean
 declare var __BUNDLER__: boolean
 declare var __RUNTIME_COMPILE__: boolean
+declare var __SSR__: boolean
 declare var __COMMIT__: string
 declare var __VERSION__: string
 

+ 24 - 5
packages/runtime-core/src/component.ts

@@ -5,8 +5,8 @@ import {
   ComponentPublicInstance,
   runtimeCompiledRenderProxyHandlers
 } from './componentProxy'
-import { ComponentPropsOptions } from './componentProps'
-import { Slots } from './componentSlots'
+import { ComponentPropsOptions, resolveProps } from './componentProps'
+import { Slots, resolveSlots } from './componentSlots'
 import { warn } from './warning'
 import {
   ErrorCodes,
@@ -34,6 +34,7 @@ import {
   currentRenderingInstance,
   markAttrsAccessed
 } from './componentRenderUtils'
+import { ShapeFlags } from '.'
 
 export type Data = { [key: string]: unknown }
 
@@ -268,10 +269,26 @@ export function validateComponentName(name: string, config: AppConfig) {
   }
 }
 
-export function setupStatefulComponent(
+export function setupComponent(
   instance: ComponentInternalInstance,
   parentSuspense: SuspenseBoundary | null
 ) {
+  const propsOptions = instance.type.props
+  const { props, children, shapeFlag } = instance.vnode
+  resolveProps(instance, props, propsOptions)
+  resolveSlots(instance, children)
+
+  // setup stateful logic
+  if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
+    return setupStatefulComponent(instance, parentSuspense)
+  }
+}
+
+function setupStatefulComponent(
+  instance: ComponentInternalInstance,
+  parentSuspense: SuspenseBoundary | null
+) {
+  let setupResult
   const Component = instance.type as ComponentOptions
 
   if (__DEV__) {
@@ -307,7 +324,7 @@ export function setupStatefulComponent(
 
     currentInstance = instance
     currentSuspense = parentSuspense
-    const setupResult = callWithErrorHandling(
+    setupResult = callWithErrorHandling(
       setup,
       instance,
       ErrorCodes.SETUP_FUNCTION,
@@ -333,6 +350,8 @@ export function setupStatefulComponent(
   } else {
     finishComponentSetup(instance, parentSuspense)
   }
+
+  return setupResult
 }
 
 export function handleSetupResult(
@@ -398,7 +417,7 @@ function finishComponentSetup(
             `does not support runtime template compilation. Either use the ` +
             `full build or pre-compile the template using Vue CLI.`
         )
-      } else {
+      } else if (!__SSR__ || !Component.ssrRender) {
         warn(
           `Component is missing${
             __RUNTIME_COMPILE__ ? ` template or` : ``

+ 3 - 0
packages/runtime-core/src/index.ts

@@ -97,6 +97,9 @@ export const camelize = _camelize as (s: string) => string
 // For integration with runtime compiler
 export { registerRuntimeCompiler } from './component'
 
+// For server-renderer
+export { createComponentInstance, setupComponent } from './component'
+
 // Types -----------------------------------------------------------------------
 
 export {

+ 3 - 12
packages/runtime-core/src/renderer.ts

@@ -13,9 +13,9 @@ import {
 import {
   ComponentInternalInstance,
   createComponentInstance,
-  setupStatefulComponent,
   Component,
-  Data
+  Data,
+  setupComponent
 } from './component'
 import {
   renderComponentRoot,
@@ -940,8 +940,6 @@ export function createRenderer<
       pushWarningContext(initialVNode)
     }
 
-    const Comp = initialVNode.type as Component
-
     // inject renderer internals for keepAlive
     if (isKeepAlive(initialVNode)) {
       const sink = instance.sink as KeepAliveSink
@@ -950,14 +948,7 @@ export function createRenderer<
     }
 
     // resolve props and slots for setup context
-    const propsOptions = Comp.props
-    resolveProps(instance, initialVNode.props, propsOptions)
-    resolveSlots(instance, initialVNode.children)
-
-    // setup stateful logic
-    if (initialVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
-      setupStatefulComponent(instance, parentSuspense)
-    }
+    setupComponent(instance, parentSuspense)
 
     // setup() is async. This component relies on async logic to be resolved
     // before proceeding

+ 1 - 1
packages/server-renderer/package.json

@@ -27,6 +27,6 @@
   },
   "homepage": "https://github.com/vuejs/vue/tree/dev/packages/server-renderer#readme",
   "peerDependencies": {
-    "@vue/runtime-dom": "3.0.0-alpha.3"
+    "vue": "3.0.0-alpha.3"
   }
 }

+ 13 - 14
packages/server-renderer/src/index.ts

@@ -2,8 +2,11 @@ import {
   App,
   Component,
   ComponentInternalInstance,
-  SuspenseBoundary
-} from '@vue/runtime-dom'
+  createComponentInstance,
+  setupComponent,
+  VNode,
+  createVNode
+} from 'vue'
 import { isString } from '@vue/shared'
 
 type SSRBuffer = SSRBufferItem[]
@@ -30,9 +33,7 @@ function createSSRBuffer() {
 export async function renderToString(app: App): Promise<string> {
   const resolvedBuffer = (await renderComponent(
     app._component,
-    app._props,
-    null,
-    null
+    app._props
   )) as ResolvedSSRBuffer
   return unrollBuffer(resolvedBuffer)
 }
@@ -52,19 +53,17 @@ function unrollBuffer(buffer: ResolvedSSRBuffer): string {
 
 export async function renderComponent(
   comp: Component,
-  props: Record<string, any> | null,
-  parentComponent: ComponentInternalInstance | null,
-  parentSuspense: SuspenseBoundary | null
+  props: Record<string, any> | null = null,
+  children: VNode['children'] = null,
+  parentComponent: ComponentInternalInstance | null = null
 ): Promise<SSRBuffer> {
   // 1. create component buffer
   const { buffer, push } = createSSRBuffer()
 
-  // 2. TODO create actual instance
-  const instance = {
-    proxy: {
-      msg: 'hello'
-    }
-  }
+  // 2. create actual instance
+  const vnode = createVNode(comp, props, children)
+  const instance = createComponentInstance(vnode, parentComponent)
+  await setupComponent(instance, null)
 
   if (typeof comp === 'function') {
     // TODO FunctionalComponent

+ 14 - 6
rollup.config.js

@@ -79,6 +79,7 @@ function createConfig(format, output, plugins = []) {
     process.env.__DEV__ === 'false' || /\.prod\.js$/.test(output.file)
   const isGlobalBuild = format === 'global'
   const isRawESMBuild = format === 'esm'
+  const isNodeBuild = format === 'cjs'
   const isBundlerESMBuild = /esm-bundler/.test(format)
   const isRuntimeCompileBuild = /vue\./.test(output.file)
 
@@ -111,14 +112,16 @@ function createConfig(format, output, plugins = []) {
   const entryFile =
     format === 'esm-bundler-runtime' ? `src/runtime.ts` : `src/index.ts`
 
+  const external =
+    isGlobalBuild || isRawESMBuild
+      ? []
+      : knownExternals.concat(Object.keys(pkg.dependencies || []))
+
   return {
     input: resolve(entryFile),
     // Global and Browser ESM builds inlines everything so that they can be
     // used alone.
-    external:
-      isGlobalBuild || isRawESMBuild
-        ? []
-        : knownExternals.concat(Object.keys(pkg.dependencies || [])),
+    external,
     plugins: [
       json({
         namedExports: false
@@ -127,9 +130,11 @@ function createConfig(format, output, plugins = []) {
       createReplacePlugin(
         isProductionBuild,
         isBundlerESMBuild,
+        // isBrowserBuild?
         (isGlobalBuild || isRawESMBuild || isBundlerESMBuild) &&
           !packageOptions.enableNonBrowserBranches,
-        isRuntimeCompileBuild
+        isRuntimeCompileBuild,
+        isNodeBuild
       ),
       ...plugins
     ],
@@ -146,7 +151,8 @@ function createReplacePlugin(
   isProduction,
   isBundlerESMBuild,
   isBrowserBuild,
-  isRuntimeCompileBuild
+  isRuntimeCompileBuild,
+  isNodeBuild
 ) {
   const replacements = {
     __COMMIT__: `"${process.env.COMMIT}"`,
@@ -164,6 +170,8 @@ function createReplacePlugin(
     __BUNDLER__: isBundlerESMBuild,
     // support compile in browser?
     __RUNTIME_COMPILE__: isRuntimeCompileBuild,
+    // is targeting Node (SSR)?
+    __SSR__: isNodeBuild,
     // support options?
     // the lean build drops options related code with buildOptions.lean: true
     __FEATURE_OPTIONS__: !packageOptions.lean && !process.env.LEAN,