wip(ssr): ssr helper codegen

This commit is contained in:
Evan You
2020-02-03 17:47:06 -05:00
parent d1d81cf1f9
commit b685805a26
18 changed files with 374 additions and 253 deletions

View File

@@ -22,20 +22,23 @@ export function compile(
template: string,
options: SSRCompilerOptions = {}
): CodegenResult {
// apply DOM-specific parsing options
options = {
mode: 'cjs',
...options,
// apply DOM-specific parsing options
...parserOptions,
...options
ssr: true,
// always prefix since compiler-ssr doesn't have size concern
prefixIdentifiers: true,
// disalbe optimizations that are unnecessary for ssr
cacheHandlers: false,
hoistStatic: false
}
const ast = baseParse(template, options)
transform(ast, {
...options,
prefixIdentifiers: true,
// disalbe optimizations that are unnecessary for ssr
cacheHandlers: false,
hoistStatic: false,
nodeTransforms: [
ssrTransformIf,
ssrTransformFor,
@@ -57,10 +60,5 @@ export function compile(
// by replacing ast.codegenNode.
ssrCodegenTransform(ast, options)
return generate(ast, {
mode: 'cjs',
...options,
ssr: true,
prefixIdentifiers: true
})
return generate(ast, options)
}

View File

@@ -1,7 +1,21 @@
import { registerRuntimeHelpers } from '@vue/compiler-dom'
export const INTERPOLATE = Symbol(`interpolate`)
export const SSR_INTERPOLATE = Symbol(`interpolate`)
export const SSR_RENDER_COMPONENT = Symbol(`renderComponent`)
export const SSR_RENDER_SLOT = Symbol(`renderSlot`)
export const SSR_RENDER_CLASS = Symbol(`renderClass`)
export const SSR_RENDER_STYLE = Symbol(`renderStyle`)
export const SSR_RENDER_PROPS = Symbol(`renderProps`)
export const SSR_RENDER_LIST = Symbol(`renderList`)
// Note: these are helpers imported from @vue/server-renderer
// make sure the names match!
registerRuntimeHelpers({
[INTERPOLATE]: `interpolate`
[SSR_INTERPOLATE]: `_interpolate`,
[SSR_RENDER_COMPONENT]: `_renderComponent`,
[SSR_RENDER_SLOT]: `_renderSlot`,
[SSR_RENDER_CLASS]: `_renderClass`,
[SSR_RENDER_STYLE]: `_renderStyle`,
[SSR_RENDER_PROPS]: `_renderProps`,
[SSR_RENDER_LIST]: `_renderList`
})

View File

@@ -14,8 +14,9 @@ import {
CallExpression
} from '@vue/compiler-dom'
import { isString, escapeHtml, NO } from '@vue/shared'
import { INTERPOLATE } from './runtimeHelpers'
import { SSR_INTERPOLATE } from './runtimeHelpers'
import { processIf } from './transforms/ssrVIf'
import { processFor } from './transforms/ssrVFor'
// Because SSR codegen output is completely different from client-side output
// (e.g. multiple elements can be concatenated into a single template literal
@@ -37,17 +38,26 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
}
ast.codegenNode = createBlockStatement(context.body)
ast.ssrHelpers = [...context.helpers]
}
export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
export function createSSRTransformContext(options: CompilerOptions) {
function createSSRTransformContext(
options: CompilerOptions,
helpers: Set<symbol> = new Set()
) {
const body: BlockStatement['body'] = []
let currentString: TemplateLiteral | null = null
return {
options,
body,
helpers,
helper<T extends symbol>(name: T): T {
helpers.add(name)
return name
},
pushStringPart(part: TemplateLiteral['elements'][0]) {
if (!currentString) {
const currentCall = createCallExpression(`_push`)
@@ -71,6 +81,13 @@ export function createSSRTransformContext(options: CompilerOptions) {
}
}
export function createChildContext(
parent: SSRTransformContext
): SSRTransformContext {
// ensure child inherits parent helpers
return createSSRTransformContext(parent.options, parent.helpers)
}
export function processChildren(
children: TemplateChildNode[],
context: SSRTransformContext
@@ -100,11 +117,13 @@ export function processChildren(
} else if (child.type === NodeTypes.TEXT) {
context.pushStringPart(escapeHtml(child.content))
} else if (child.type === NodeTypes.INTERPOLATION) {
context.pushStringPart(createCallExpression(INTERPOLATE, [child.content]))
context.pushStringPart(
createCallExpression(context.helper(SSR_INTERPOLATE), [child.content])
)
} else if (child.type === NodeTypes.IF) {
processIf(child, context)
} else if (child.type === NodeTypes.FOR) {
// TODO
processFor(child, context)
}
}
}

View File

@@ -1,3 +1,16 @@
import { NodeTransform } from '@vue/compiler-dom'
import {
createStructuralDirectiveTransform,
ForNode,
processForNode
} from '@vue/compiler-dom'
import { SSRTransformContext } from '../ssrCodegenTransform'
export const ssrTransformFor: NodeTransform = () => {}
// Plugin for the first transform pass, which simply constructs the AST node
export const ssrTransformFor = createStructuralDirectiveTransform(
'for',
processForNode
)
// This is called during the 2nd transform pass to construct the SSR-sepcific
// codegen nodes.
export function processFor(node: ForNode, context: SSRTransformContext) {}

View File

@@ -11,12 +11,11 @@ import {
} from '@vue/compiler-dom'
import {
SSRTransformContext,
createSSRTransformContext,
createChildContext,
processChildren
} from '../ssrCodegenTransform'
// This is the plugin for the first transform pass, which simply constructs the
// if node and its branches.
// Plugin for the first transform pass, which simply constructs the AST node
export const ssrTransformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/,
processIfBranches
@@ -64,7 +63,7 @@ function processIfBranch(
// TODO optimize away nested fragments when the only child is a ForNode
const needFragmentWrapper =
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
const childContext = createSSRTransformContext(context.options)
const childContext = createChildContext(context)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}