test(compiler-ssr): v-for tests

This commit is contained in:
Evan You
2020-02-03 20:47:41 -05:00
parent 93c37b94f2
commit 8cf6b5731d
5 changed files with 172 additions and 12 deletions

View File

@@ -0,0 +1,126 @@
import { compile } from '../src'
describe('ssr: v-for', () => {
test('basic', () => {
expect(compile(`<div v-for="i in list" />`).code).toMatchInlineSnapshot(`
"const { _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<div></div>\`)
})
_push(\`<!---->\`)
}"
`)
})
test('nested content', () => {
expect(compile(`<div v-for="i in list">foo<span>bar</span></div>`).code)
.toMatchInlineSnapshot(`
"const { _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<div>foo<span>bar</span></div>\`)
})
_push(\`<!---->\`)
}"
`)
})
test('nested v-for', () => {
expect(
compile(
`<div v-for="row, i in list">` +
`<div v-for="j in row">{{ i }},{{ j }}</div>` +
`</div>`
).code
).toMatchInlineSnapshot(`
"const { _interpolate, _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (row, i) => {
_push(\`<div><!---->\`)
_renderList(row, (j) => {
_push(\`<div>\${_interpolate(i)},\${_interpolate(j)}</div>\`)
})
_push(\`<!----></div>\`)
})
_push(\`<!---->\`)
}"
`)
})
test('template v-for (text)', () => {
expect(compile(`<template v-for="i in list">{{ i }}</template>`).code)
.toMatchInlineSnapshot(`
"const { _interpolate, _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<!---->\${_interpolate(i)}<!---->\`)
})
_push(\`<!---->\`)
}"
`)
})
test('template v-for (single element)', () => {
expect(
compile(`<template v-for="i in list"><span>{{ i }}</span></template>`)
.code
).toMatchInlineSnapshot(`
"const { _interpolate, _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<span>\${_interpolate(i)}</span>\`)
})
_push(\`<!---->\`)
}"
`)
})
test('template v-for (multi element)', () => {
expect(
compile(
`<template v-for="i in list"><span>{{ i }}</span><span>{{ i + 1 }}</span></template>`
).code
).toMatchInlineSnapshot(`
"const { _interpolate, _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<!----><span>\${_interpolate(i)}</span><span>\${_interpolate(i + 1)}</span><!---->\`)
})
_push(\`<!---->\`)
}"
`)
})
test('render loop args should not be prefixed', () => {
const { code } = compile(
`<div v-for="{ foo }, index in list">{{ foo + bar + index }}</div>`
)
expect(code).toMatch(`_ctx.bar`)
expect(code).not.toMatch(`_ctx.foo`)
expect(code).not.toMatch(`_ctx.index`)
expect(code).toMatchInlineSnapshot(`
"const { _interpolate, _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
_push(\`<!---->\`)
_renderList(_ctx.list, ({ foo }, index) => {
_push(\`<div>\${_interpolate(foo + _ctx.bar + index)}</div>\`)
})
_push(\`<!---->\`)
}"
`)
})
})

View File

@@ -119,7 +119,23 @@ describe('ssr: v-if', () => {
})
test('<template v-if> (with v-for inside)', () => {
// TODO should not contain nested fragments
expect(
compile(`<template v-if="foo"><div v-for="i in list"/></template>`).code
).toMatchInlineSnapshot(`
"const { _renderList } = require(\\"@vue/server-renderer\\")
return function ssrRender(_ctx, _push, _parent) {
if (_ctx.foo) {
_push(\`<!---->\`)
_renderList(_ctx.list, (i) => {
_push(\`<div></div>\`)
})
_push(\`<!---->\`)
} else {
_push(\`<!---->\`)
}
}"
`)
})
test('<template v-if> + normal v-else', () => {

View File

@@ -5,7 +5,8 @@ import {
createCallExpression,
createFunctionExpression,
createForLoopParams,
createBlockStatement
createBlockStatement,
NodeTypes
} from '@vue/compiler-dom'
import {
SSRTransformContext,
@@ -23,16 +24,28 @@ export const ssrTransformFor = createStructuralDirectiveTransform(
// This is called during the 2nd transform pass to construct the SSR-sepcific
// codegen nodes.
export function processFor(node: ForNode, context: SSRTransformContext) {
const childContext = createChildContext(context)
const needFragmentWrapper =
node.children.length !== 1 || node.children[0].type !== NodeTypes.ELEMENT
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(node.children, childContext)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
const renderLoop = createFunctionExpression(
createForLoopParams(node.parseResult)
)
const childContext = createChildContext(context)
processChildren(node.children, childContext)
renderLoop.body = createBlockStatement(childContext.body)
// v-for always renders a fragment
context.pushStringPart(`<!---->`)
context.pushStatement(
createCallExpression(context.helper(SSR_RENDER_LIST), [
node.source,
renderLoop
])
)
context.pushStringPart(`<!---->`)
}

View File

@@ -59,15 +59,15 @@ function processIfBranch(
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
(children.length !== 1 || children[0].type !== NodeTypes.ELEMENT) &&
// optimize away nested fragments when the only child is a ForNode
!(children.length === 1 && children[0].type === NodeTypes.FOR)
const childContext = createChildContext(context)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}
processChildren(branch.children, childContext)
processChildren(children, childContext)
if (needFragmentWrapper) {
childContext.pushStringPart(`<!---->`)
}