fix(compiler-core): fix keep-alive when used in templates

fix #715
This commit is contained in:
Evan You 2020-02-10 16:49:37 -05:00
parent 6d10a6c772
commit ade07c64a1
2 changed files with 46 additions and 24 deletions

View File

@ -346,18 +346,26 @@ describe('compiler: element transform', () => {
test('should handle <KeepAlive>', () => { test('should handle <KeepAlive>', () => {
function assert(tag: string) { function assert(tag: string) {
const { root, node } = parseWithElementTransform( const root = parse(`<div><${tag}><span /></${tag}></div>`)
`<${tag}><span /></${tag}>` transform(root, {
) nodeTransforms: [transformElement, transformText]
})
expect(root.components.length).toBe(0) expect(root.components.length).toBe(0)
expect(root.helpers).toContain(KEEP_ALIVE) expect(root.helpers).toContain(KEEP_ALIVE)
expect(node.callee).toBe(CREATE_VNODE) const node = (root.children[0] as any).children[0].codegenNode
expect(node.arguments).toMatchObject([ expect(node.type).toBe(NodeTypes.JS_SEQUENCE_EXPRESSION)
KEEP_ALIVE, expect(node.expressions[1]).toMatchObject({
`null`, type: NodeTypes.JS_CALL_EXPRESSION,
// keep-alive should not compile content to slots callee: CREATE_BLOCK, // should be forced into a block
[{ type: NodeTypes.ELEMENT, tag: 'span' }] arguments: [
]) KEEP_ALIVE,
`null`,
// keep-alive should not compile content to slots
[{ type: NodeTypes.ELEMENT, tag: 'span' }],
// should get a dynamic slots flag to force updates
genFlagText(PatchFlags.DYNAMIC_SLOTS)
]
})
} }
assert(`keep-alive`) assert(`keep-alive`)

View File

@ -67,7 +67,7 @@ export const transformElement: NodeTransform = (node, context) => {
// updates inside get proper isSVG flag at runtime. (#639, #643) // updates inside get proper isSVG flag at runtime. (#639, #643)
// This is technically web-specific, but splitting the logic out of core // This is technically web-specific, but splitting the logic out of core
// leads to too much unnecessary complexity. // leads to too much unnecessary complexity.
const shouldUseBlock = let shouldUseBlock =
!isComponent && (tag === 'svg' || tag === 'foreignObject') !isComponent && (tag === 'svg' || tag === 'foreignObject')
const nodeType = isComponent const nodeType = isComponent
@ -101,21 +101,35 @@ export const transformElement: NodeTransform = (node, context) => {
args.push(`null`) args.push(`null`)
} }
if (__DEV__ && nodeType === KEEP_ALIVE && node.children.length > 1) { if (nodeType === KEEP_ALIVE) {
context.onError( // Although a built-in component, we compile KeepAlive with raw children
createCompilerError(ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN, { // instead of slot functions so that it can be used inside Transition
start: node.children[0].loc.start, // or other Transition-wrapping HOCs.
end: node.children[node.children.length - 1].loc.end, // To ensure correct updates with block optimizations, we need to:
source: '' // 1. Force keep-alive into a block. This avoids its children being
}) // collected by a parent block.
) shouldUseBlock = true
// 2. Force keep-alive to always be updated, since it uses raw children.
patchFlag |= PatchFlags.DYNAMIC_SLOTS
if (__DEV__ && node.children.length > 1) {
context.onError(
createCompilerError(ErrorCodes.X_KEEP_ALIVE_INVALID_CHILDREN, {
start: node.children[0].loc.start,
end: node.children[node.children.length - 1].loc.end,
source: ''
})
)
}
} }
// Portal & KeepAlive should have normal children instead of slots const shouldBuildAsSlots =
// Portal is not a real component has dedicated handling in the renderer isComponent &&
// KeepAlive should not track its own deps so that it can be used inside // Portal is not a real component has dedicated handling in the renderer
// Transition nodeType !== PORTAL &&
if (isComponent && nodeType !== PORTAL && nodeType !== KEEP_ALIVE) { // explained above.
nodeType !== KEEP_ALIVE
if (shouldBuildAsSlots) {
const { slots, hasDynamicSlots } = buildSlots(node, context) const { slots, hasDynamicSlots } = buildSlots(node, context)
args.push(slots) args.push(slots)
if (hasDynamicSlots) { if (hasDynamicSlots) {