wip(ssr): render real components

This commit is contained in:
Evan You 2020-01-23 22:23:10 -05:00
parent da25517377
commit cee36ad028
8 changed files with 60 additions and 38 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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