fix(compiler-core): generate incremental keys for v-if/else-if/else chains (#1589)
fix #1587
This commit is contained in:
parent
46158b4591
commit
64c7b2f9ce
@ -14,6 +14,46 @@ return function render(_ctx, _cache) {
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if codegen increasing key: v-if + v-else-if + v-else 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||||
|
ok
|
||||||
|
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
|
||||||
|
: (_openBlock(), _createBlock(\\"p\\", { key: 1 })),
|
||||||
|
another
|
||||||
|
? (_openBlock(), _createBlock(\\"div\\", { key: 2 }))
|
||||||
|
: orNot
|
||||||
|
? (_openBlock(), _createBlock(\\"p\\", { key: 3 }))
|
||||||
|
: (_openBlock(), _createBlock(\\"p\\", { key: 4 }))
|
||||||
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: v-if codegen multiple v-if that are sibling nodes should have different keys 1`] = `
|
||||||
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
return function render(_ctx, _cache) {
|
||||||
|
with (_ctx) {
|
||||||
|
const { createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode, Fragment: _Fragment } = _Vue
|
||||||
|
|
||||||
|
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||||
|
ok
|
||||||
|
? (_openBlock(), _createBlock(\\"div\\", { key: 0 }))
|
||||||
|
: _createCommentVNode(\\"v-if\\", true),
|
||||||
|
orNot
|
||||||
|
? (_openBlock(), _createBlock(\\"p\\", { key: 1 }))
|
||||||
|
: _createCommentVNode(\\"v-if\\", true)
|
||||||
|
], 64 /* STABLE_FRAGMENT */))
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: v-if codegen template v-if 1`] = `
|
exports[`compiler: v-if codegen template v-if 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
@ -29,7 +29,8 @@ import { createObjectMatcher } from '../testUtils'
|
|||||||
function parseWithIfTransform(
|
function parseWithIfTransform(
|
||||||
template: string,
|
template: string,
|
||||||
options: CompilerOptions = {},
|
options: CompilerOptions = {},
|
||||||
returnIndex: number = 0
|
returnIndex: number = 0,
|
||||||
|
childrenLen: number = 1
|
||||||
) {
|
) {
|
||||||
const ast = parse(template, options)
|
const ast = parse(template, options)
|
||||||
transform(ast, {
|
transform(ast, {
|
||||||
@ -37,8 +38,10 @@ function parseWithIfTransform(
|
|||||||
...options
|
...options
|
||||||
})
|
})
|
||||||
if (!options.onError) {
|
if (!options.onError) {
|
||||||
expect(ast.children.length).toBe(1)
|
expect(ast.children.length).toBe(childrenLen)
|
||||||
expect(ast.children[0].type).toBe(NodeTypes.IF)
|
for (let i = 0; i < childrenLen; i++) {
|
||||||
|
expect(ast.children[i].type).toBe(NodeTypes.IF)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
root: ast,
|
root: ast,
|
||||||
@ -459,6 +462,68 @@ describe('compiler: v-if', () => {
|
|||||||
expect(generate(root).code).toMatchSnapshot()
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('multiple v-if that are sibling nodes should have different keys', () => {
|
||||||
|
const { root } = parseWithIfTransform(
|
||||||
|
`<div v-if="ok"/><p v-if="orNot"/>`,
|
||||||
|
{},
|
||||||
|
0 /* returnIndex, just give the default value */,
|
||||||
|
2 /* childrenLen */
|
||||||
|
)
|
||||||
|
|
||||||
|
const ifNode = root.children[0] as IfNode & {
|
||||||
|
codegenNode: IfConditionalExpression
|
||||||
|
}
|
||||||
|
expect(ifNode.codegenNode.consequent).toMatchObject({
|
||||||
|
tag: `"div"`,
|
||||||
|
props: createObjectMatcher({ key: `[0]` })
|
||||||
|
})
|
||||||
|
const ifNode2 = root.children[1] as IfNode & {
|
||||||
|
codegenNode: IfConditionalExpression
|
||||||
|
}
|
||||||
|
expect(ifNode2.codegenNode.consequent).toMatchObject({
|
||||||
|
tag: `"p"`,
|
||||||
|
props: createObjectMatcher({ key: `[1]` })
|
||||||
|
})
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('increasing key: v-if + v-else-if + v-else', () => {
|
||||||
|
const { root } = parseWithIfTransform(
|
||||||
|
`<div v-if="ok"/><p v-else/><div v-if="another"/><p v-else-if="orNot"/><p v-else/>`,
|
||||||
|
{},
|
||||||
|
0 /* returnIndex, just give the default value */,
|
||||||
|
2 /* childrenLen */
|
||||||
|
)
|
||||||
|
const ifNode = root.children[0] as IfNode & {
|
||||||
|
codegenNode: IfConditionalExpression
|
||||||
|
}
|
||||||
|
expect(ifNode.codegenNode.consequent).toMatchObject({
|
||||||
|
tag: `"div"`,
|
||||||
|
props: createObjectMatcher({ key: `[0]` })
|
||||||
|
})
|
||||||
|
expect(ifNode.codegenNode.alternate).toMatchObject({
|
||||||
|
tag: `"p"`,
|
||||||
|
props: createObjectMatcher({ key: `[1]` })
|
||||||
|
})
|
||||||
|
const ifNode2 = root.children[1] as IfNode & {
|
||||||
|
codegenNode: IfConditionalExpression
|
||||||
|
}
|
||||||
|
expect(ifNode2.codegenNode.consequent).toMatchObject({
|
||||||
|
tag: `"div"`,
|
||||||
|
props: createObjectMatcher({ key: `[2]` })
|
||||||
|
})
|
||||||
|
const branch = ifNode2.codegenNode.alternate as IfConditionalExpression
|
||||||
|
expect(branch.consequent).toMatchObject({
|
||||||
|
tag: `"p"`,
|
||||||
|
props: createObjectMatcher({ key: `[3]` })
|
||||||
|
})
|
||||||
|
expect(branch.alternate).toMatchObject({
|
||||||
|
tag: `"p"`,
|
||||||
|
props: createObjectMatcher({ key: `[4]` })
|
||||||
|
})
|
||||||
|
expect(generate(root).code).toMatchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('key injection (only v-bind)', () => {
|
test('key injection (only v-bind)', () => {
|
||||||
const {
|
const {
|
||||||
node: { codegenNode }
|
node: { codegenNode }
|
||||||
|
@ -37,13 +37,26 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
/^(if|else|else-if)$/,
|
/^(if|else|else-if)$/,
|
||||||
(node, dir, context) => {
|
(node, dir, context) => {
|
||||||
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
|
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
|
||||||
|
// #1587: We need to dynamically increment the key based on the current
|
||||||
|
// node's sibling nodes, since chained v-if/else branches are
|
||||||
|
// rendered at the same depth
|
||||||
|
const siblings = context.parent!.children
|
||||||
|
let i = siblings.indexOf(ifNode)
|
||||||
|
let key = 0
|
||||||
|
while (i-- >= 0) {
|
||||||
|
const sibling = siblings[i]
|
||||||
|
if (sibling && sibling.type === NodeTypes.IF) {
|
||||||
|
key += sibling.branches.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Exit callback. Complete the codegenNode when all children have been
|
// Exit callback. Complete the codegenNode when all children have been
|
||||||
// transformed.
|
// transformed.
|
||||||
return () => {
|
return () => {
|
||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
ifNode.codegenNode = createCodegenNodeForBranch(
|
ifNode.codegenNode = createCodegenNodeForBranch(
|
||||||
branch,
|
branch,
|
||||||
0,
|
key,
|
||||||
context
|
context
|
||||||
) as IfConditionalExpression
|
) as IfConditionalExpression
|
||||||
} else {
|
} else {
|
||||||
@ -57,7 +70,7 @@ export const transformIf = createStructuralDirectiveTransform(
|
|||||||
}
|
}
|
||||||
parentCondition.alternate = createCodegenNodeForBranch(
|
parentCondition.alternate = createCodegenNodeForBranch(
|
||||||
branch,
|
branch,
|
||||||
ifNode.branches.length - 1,
|
key + ifNode.branches.length - 1,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user