wip(compiler-ssr): v-if
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { getCompiledString } from './utils'
|
||||
|
||||
describe('element', () => {
|
||||
describe('ssr: element', () => {
|
||||
test('basic elements', () => {
|
||||
expect(getCompiledString(`<div></div>`)).toMatchInlineSnapshot(
|
||||
`"\`<div></div>\`"`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getCompiledString } from './utils'
|
||||
|
||||
describe('text', () => {
|
||||
describe('ssr: text', () => {
|
||||
test('static text', () => {
|
||||
expect(getCompiledString(`foo`)).toMatchInlineSnapshot(`"\`foo\`"`)
|
||||
})
|
||||
|
||||
141
packages/compiler-ssr/__tests__/ssrVIf.spec.ts
Normal file
141
packages/compiler-ssr/__tests__/ssrVIf.spec.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { compile } from '../src'
|
||||
|
||||
describe('ssr: v-if', () => {
|
||||
test('basic', () => {
|
||||
expect(compile(`<div v-if="foo"></div>`).code).toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div></div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('with nested content', () => {
|
||||
expect(compile(`<div v-if="foo">hello<span>ok</span></div>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div>hello<span>ok</span></div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('v-if + v-else', () => {
|
||||
expect(compile(`<div v-if="foo"/><span v-else/>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div></div>\`)
|
||||
} else {
|
||||
_push(\`<span></span>\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('v-if + v-else-if', () => {
|
||||
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div></div>\`)
|
||||
} else if (_ctx.bar) {
|
||||
_push(\`<span></span>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('v-if + v-else-if + v-else', () => {
|
||||
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/><p v-else/>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div></div>\`)
|
||||
} else if (_ctx.bar) {
|
||||
_push(\`<span></span>\`)
|
||||
} else {
|
||||
_push(\`<p></p>\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('<template v-if> (text)', () => {
|
||||
expect(compile(`<template v-if="foo">hello</template>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<!---->hello<!---->\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('<template v-if> (single element)', () => {
|
||||
// single element should not wrap with fragment
|
||||
expect(compile(`<template v-if="foo"><div>hi</div></template>`).code)
|
||||
.toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<div>hi</div>\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('<template v-if> (multiple element)', () => {
|
||||
expect(
|
||||
compile(`<template v-if="foo"><div>hi</div><div>ho</div></template>`).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||
} else {
|
||||
_push(\`<!---->\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('<template v-if> (with v-for inside)', () => {
|
||||
// TODO should not contain nested fragments
|
||||
})
|
||||
|
||||
test('<template v-if> + normal v-else', () => {
|
||||
expect(
|
||||
compile(
|
||||
`<template v-if="foo"><div>hi</div><div>ho</div></template><div v-else/>`
|
||||
).code
|
||||
).toMatchInlineSnapshot(`
|
||||
"
|
||||
return function ssrRender(_ctx, _push, _parent) {
|
||||
if (_ctx.foo) {
|
||||
_push(\`<!----><div>hi</div><div>ho</div><!---->\`)
|
||||
} else {
|
||||
_push(\`<div></div>\`)
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
RootNode,
|
||||
BlockStatement,
|
||||
CallExpression,
|
||||
TemplateLiteral,
|
||||
createCallExpression,
|
||||
createTemplateLiteral,
|
||||
@@ -10,10 +9,13 @@ import {
|
||||
ElementTypes,
|
||||
createBlockStatement,
|
||||
CompilerOptions,
|
||||
isText
|
||||
isText,
|
||||
IfStatement,
|
||||
CallExpression
|
||||
} from '@vue/compiler-dom'
|
||||
import { isString, escapeHtml, NO } from '@vue/shared'
|
||||
import { INTERPOLATE } from './runtimeHelpers'
|
||||
import { processIf } from './transforms/ssrVIf'
|
||||
|
||||
// Because SSR codegen output is completely different from client-side output
|
||||
// (e.g. multiple elements can be concatenated into a single template literal
|
||||
@@ -37,22 +39,19 @@ export function ssrCodegenTransform(ast: RootNode, options: CompilerOptions) {
|
||||
ast.codegenNode = createBlockStatement(context.body)
|
||||
}
|
||||
|
||||
type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
||||
export type SSRTransformContext = ReturnType<typeof createSSRTransformContext>
|
||||
|
||||
function createSSRTransformContext(options: CompilerOptions) {
|
||||
export function createSSRTransformContext(options: CompilerOptions) {
|
||||
const body: BlockStatement['body'] = []
|
||||
let currentCall: CallExpression | null = null
|
||||
let currentString: TemplateLiteral | null = null
|
||||
|
||||
return {
|
||||
options,
|
||||
body,
|
||||
pushStringPart(part: TemplateLiteral['elements'][0]) {
|
||||
if (!currentCall) {
|
||||
currentCall = createCallExpression(`_push`)
|
||||
body.push(currentCall)
|
||||
}
|
||||
if (!currentString) {
|
||||
const currentCall = createCallExpression(`_push`)
|
||||
body.push(currentCall)
|
||||
currentString = createTemplateLiteral([])
|
||||
currentCall.arguments.push(currentString)
|
||||
}
|
||||
@@ -63,11 +62,16 @@ function createSSRTransformContext(options: CompilerOptions) {
|
||||
} else {
|
||||
bufferedElements.push(part)
|
||||
}
|
||||
},
|
||||
pushStatement(statement: IfStatement | CallExpression) {
|
||||
// close current string
|
||||
currentString = null
|
||||
body.push(statement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processChildren(
|
||||
export function processChildren(
|
||||
children: TemplateChildNode[],
|
||||
context: SSRTransformContext
|
||||
) {
|
||||
@@ -98,7 +102,7 @@ function processChildren(
|
||||
} else if (child.type === NodeTypes.INTERPOLATION) {
|
||||
context.pushStringPart(createCallExpression(INTERPOLATE, [child.content]))
|
||||
} else if (child.type === NodeTypes.IF) {
|
||||
// TODO
|
||||
processIf(child, context)
|
||||
} else if (child.type === NodeTypes.FOR) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@@ -8,33 +8,6 @@ import {
|
||||
} from '@vue/compiler-dom'
|
||||
import { escapeHtml } from '@vue/shared'
|
||||
|
||||
/*
|
||||
## Simple Element
|
||||
|
||||
``` html
|
||||
<div></div>
|
||||
```
|
||||
``` js
|
||||
return function render(_ctx, _push, _parent) {
|
||||
_push(`<div></div>`)
|
||||
}
|
||||
```
|
||||
|
||||
## Consecutive Elements
|
||||
|
||||
``` html
|
||||
<div>
|
||||
<span></span>
|
||||
</div>
|
||||
<div></div>
|
||||
```
|
||||
``` js
|
||||
return function render(_ctx, _push, _parent) {
|
||||
_push(`<div><span></span></div><div></div>`)
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
export const ssrTransformElement: NodeTransform = (node, context) => {
|
||||
if (
|
||||
node.type === NodeTypes.ELEMENT &&
|
||||
|
||||
@@ -1,3 +1,76 @@
|
||||
import { NodeTransform } from '@vue/compiler-dom'
|
||||
import {
|
||||
createStructuralDirectiveTransform,
|
||||
processIfBranches,
|
||||
IfNode,
|
||||
createIfStatement,
|
||||
createBlockStatement,
|
||||
createCallExpression,
|
||||
IfBranchNode,
|
||||
BlockStatement,
|
||||
NodeTypes
|
||||
} from '@vue/compiler-dom'
|
||||
import {
|
||||
SSRTransformContext,
|
||||
createSSRTransformContext,
|
||||
processChildren
|
||||
} from '../ssrCodegenTransform'
|
||||
|
||||
export const ssrTransformIf: NodeTransform = () => {}
|
||||
// This is the plugin for the first transform pass, which simply constructs the
|
||||
// if node and its branches.
|
||||
export const ssrTransformIf = createStructuralDirectiveTransform(
|
||||
/^(if|else|else-if)$/,
|
||||
processIfBranches
|
||||
)
|
||||
|
||||
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
||||
// codegen nodes.
|
||||
export function processIf(node: IfNode, context: SSRTransformContext) {
|
||||
const [rootBranch] = node.branches
|
||||
const ifStatement = createIfStatement(
|
||||
rootBranch.condition!,
|
||||
processIfBranch(rootBranch, context)
|
||||
)
|
||||
context.pushStatement(ifStatement)
|
||||
|
||||
let currentIf = ifStatement
|
||||
for (let i = 1; i < node.branches.length; i++) {
|
||||
const branch = node.branches[i]
|
||||
const branchBlockStatement = processIfBranch(branch, context)
|
||||
if (branch.condition) {
|
||||
// else-if
|
||||
currentIf = currentIf.alternate = createIfStatement(
|
||||
branch.condition,
|
||||
branchBlockStatement
|
||||
)
|
||||
} else {
|
||||
// else
|
||||
currentIf.alternate = branchBlockStatement
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentIf.alternate) {
|
||||
currentIf.alternate = createBlockStatement([
|
||||
createCallExpression(`_push`, ['`<!---->`'])
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function processIfBranch(
|
||||
branch: IfBranchNode,
|
||||
context: SSRTransformContext
|
||||
): BlockStatement {
|
||||
const { children } = branch
|
||||
const firstChild = children[0]
|
||||
// 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)
|
||||
if (needFragmentWrapper) {
|
||||
childContext.pushStringPart(`<!---->`)
|
||||
}
|
||||
processChildren(branch.children, childContext)
|
||||
if (needFragmentWrapper) {
|
||||
childContext.pushStringPart(`<!---->`)
|
||||
}
|
||||
return createBlockStatement(childContext.body)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user