test(compiler-ssr): v-for tests
This commit is contained in:
parent
93c37b94f2
commit
8cf6b5731d
@ -20,7 +20,8 @@ import {
|
|||||||
SlotOutletNode,
|
SlotOutletNode,
|
||||||
ElementNode,
|
ElementNode,
|
||||||
DirectiveNode,
|
DirectiveNode,
|
||||||
ForNode
|
ForNode,
|
||||||
|
PlainElementNode
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
@ -70,6 +71,9 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
// finish the codegen now that all children have been traversed
|
// finish the codegen now that all children have been traversed
|
||||||
let childBlock
|
let childBlock
|
||||||
const isTemplate = isTemplateNode(node)
|
const isTemplate = isTemplateNode(node)
|
||||||
|
const { children } = forNode
|
||||||
|
const needFragmentWrapper =
|
||||||
|
children.length > 1 || children[0].type !== NodeTypes.ELEMENT
|
||||||
const slotOutlet = isSlotOutlet(node)
|
const slotOutlet = isSlotOutlet(node)
|
||||||
? node
|
? node
|
||||||
: isTemplate &&
|
: isTemplate &&
|
||||||
@ -94,8 +98,8 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
// the props for renderSlot is passed as the 3rd argument.
|
// the props for renderSlot is passed as the 3rd argument.
|
||||||
injectProp(childBlock, keyProperty, context)
|
injectProp(childBlock, keyProperty, context)
|
||||||
}
|
}
|
||||||
} else if (isTemplate) {
|
} else if (needFragmentWrapper) {
|
||||||
// <template v-for="...">
|
// <template v-for="..."> with text or multi-elements
|
||||||
// should generate a fragment block for each loop
|
// should generate a fragment block for each loop
|
||||||
childBlock = createBlockExpression(
|
childBlock = createBlockExpression(
|
||||||
createCallExpression(helper(CREATE_BLOCK), [
|
createCallExpression(helper(CREATE_BLOCK), [
|
||||||
@ -111,7 +115,8 @@ export const transformFor = createStructuralDirectiveTransform(
|
|||||||
} else {
|
} else {
|
||||||
// Normal element v-for. Directly use the child's codegenNode
|
// Normal element v-for. Directly use the child's codegenNode
|
||||||
// arguments, but replace createVNode() with createBlock()
|
// arguments, but replace createVNode() with createBlock()
|
||||||
let codegenNode = node.codegenNode as ElementCodegenNode
|
let codegenNode = (children[0] as PlainElementNode)
|
||||||
|
.codegenNode as ElementCodegenNode
|
||||||
if (codegenNode.callee === WITH_DIRECTIVES) {
|
if (codegenNode.callee === WITH_DIRECTIVES) {
|
||||||
codegenNode.arguments[0].callee = helper(CREATE_BLOCK)
|
codegenNode.arguments[0].callee = helper(CREATE_BLOCK)
|
||||||
} else {
|
} else {
|
||||||
|
126
packages/compiler-ssr/__tests__/ssrVFor.spec.ts
Normal file
126
packages/compiler-ssr/__tests__/ssrVFor.spec.ts
Normal 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(\`<!---->\`)
|
||||||
|
}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
@ -119,7 +119,23 @@ describe('ssr: v-if', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('<template v-if> (with v-for inside)', () => {
|
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', () => {
|
test('<template v-if> + normal v-else', () => {
|
||||||
|
@ -5,7 +5,8 @@ import {
|
|||||||
createCallExpression,
|
createCallExpression,
|
||||||
createFunctionExpression,
|
createFunctionExpression,
|
||||||
createForLoopParams,
|
createForLoopParams,
|
||||||
createBlockStatement
|
createBlockStatement,
|
||||||
|
NodeTypes
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import {
|
import {
|
||||||
SSRTransformContext,
|
SSRTransformContext,
|
||||||
@ -23,16 +24,28 @@ export const ssrTransformFor = createStructuralDirectiveTransform(
|
|||||||
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
// This is called during the 2nd transform pass to construct the SSR-sepcific
|
||||||
// codegen nodes.
|
// codegen nodes.
|
||||||
export function processFor(node: ForNode, context: SSRTransformContext) {
|
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(
|
const renderLoop = createFunctionExpression(
|
||||||
createForLoopParams(node.parseResult)
|
createForLoopParams(node.parseResult)
|
||||||
)
|
)
|
||||||
const childContext = createChildContext(context)
|
|
||||||
processChildren(node.children, childContext)
|
|
||||||
renderLoop.body = createBlockStatement(childContext.body)
|
renderLoop.body = createBlockStatement(childContext.body)
|
||||||
|
|
||||||
|
// v-for always renders a fragment
|
||||||
|
context.pushStringPart(`<!---->`)
|
||||||
context.pushStatement(
|
context.pushStatement(
|
||||||
createCallExpression(context.helper(SSR_RENDER_LIST), [
|
createCallExpression(context.helper(SSR_RENDER_LIST), [
|
||||||
node.source,
|
node.source,
|
||||||
renderLoop
|
renderLoop
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
context.pushStringPart(`<!---->`)
|
||||||
}
|
}
|
||||||
|
@ -59,15 +59,15 @@ function processIfBranch(
|
|||||||
context: SSRTransformContext
|
context: SSRTransformContext
|
||||||
): BlockStatement {
|
): BlockStatement {
|
||||||
const { children } = branch
|
const { children } = branch
|
||||||
const firstChild = children[0]
|
|
||||||
// TODO optimize away nested fragments when the only child is a ForNode
|
|
||||||
const needFragmentWrapper =
|
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)
|
const childContext = createChildContext(context)
|
||||||
if (needFragmentWrapper) {
|
if (needFragmentWrapper) {
|
||||||
childContext.pushStringPart(`<!---->`)
|
childContext.pushStringPart(`<!---->`)
|
||||||
}
|
}
|
||||||
processChildren(branch.children, childContext)
|
processChildren(children, childContext)
|
||||||
if (needFragmentWrapper) {
|
if (needFragmentWrapper) {
|
||||||
childContext.pushStringPart(`<!---->`)
|
childContext.pushStringPart(`<!---->`)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user