fix: bail stringification for slots

fix #1281, close #1286
This commit is contained in:
Evan You 2020-06-10 14:31:51 -04:00
parent fbaf52ae9f
commit 9b5d13e598
4 changed files with 59 additions and 12 deletions

View File

@ -1,4 +1,4 @@
import { ElementNode, Namespace, TemplateChildNode } from './ast' import { ElementNode, Namespace, TemplateChildNode, ParentNode } from './ast'
import { TextModes } from './parse' import { TextModes } from './parse'
import { CompilerError } from './errors' import { CompilerError } from './errors'
import { import {
@ -53,7 +53,8 @@ export interface ParserOptions {
export type HoistTransform = ( export type HoistTransform = (
children: TemplateChildNode[], children: TemplateChildNode[],
context: TransformContext context: TransformContext,
parent: ParentNode
) => void ) => void
export interface TransformOptions { export interface TransformOptions {

View File

@ -8,7 +8,8 @@ import {
ComponentNode, ComponentNode,
TemplateNode, TemplateNode,
ElementNode, ElementNode,
VNodeCall VNodeCall,
ParentNode
} from '../ast' } from '../ast'
import { TransformContext } from '../transform' import { TransformContext } from '../transform'
import { PatchFlags, isString, isSymbol } from '@vue/shared' import { PatchFlags, isString, isSymbol } from '@vue/shared'
@ -16,7 +17,7 @@ import { isSlotOutlet, findProp } from '../utils'
export function hoistStatic(root: RootNode, context: TransformContext) { export function hoistStatic(root: RootNode, context: TransformContext) {
walk( walk(
root.children, root,
context, context,
new Map(), new Map(),
// Root node is unfortunately non-hoistable due to potential parent // Root node is unfortunately non-hoistable due to potential parent
@ -44,7 +45,7 @@ const enum StaticType {
} }
function walk( function walk(
children: TemplateChildNode[], node: ParentNode,
context: TransformContext, context: TransformContext,
resultCache: Map<TemplateChildNode, StaticType>, resultCache: Map<TemplateChildNode, StaticType>,
doNotHoistNode: boolean = false doNotHoistNode: boolean = false
@ -60,6 +61,7 @@ function walk(
// stringficiation threshold is met. // stringficiation threshold is met.
let hasRuntimeConstant = false let hasRuntimeConstant = false
const { children } = node
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
const child = children[i] const child = children[i]
// only plain elements & text calls are eligible for hoisting. // only plain elements & text calls are eligible for hoisting.
@ -114,21 +116,25 @@ function walk(
// walk further // walk further
if (child.type === NodeTypes.ELEMENT) { if (child.type === NodeTypes.ELEMENT) {
walk(child.children, context, resultCache) walk(child, context, resultCache)
} else if (child.type === NodeTypes.FOR) { } else if (child.type === NodeTypes.FOR) {
// Do not hoist v-for single child because it has to be a block // Do not hoist v-for single child because it has to be a block
walk(child.children, context, resultCache, child.children.length === 1) walk(child, context, resultCache, child.children.length === 1)
} else if (child.type === NodeTypes.IF) { } else if (child.type === NodeTypes.IF) {
for (let i = 0; i < child.branches.length; i++) { for (let i = 0; i < child.branches.length; i++) {
const branchChildren = child.branches[i].children
// Do not hoist v-if single child because it has to be a block // Do not hoist v-if single child because it has to be a block
walk(branchChildren, context, resultCache, branchChildren.length === 1) walk(
child.branches[i],
context,
resultCache,
child.branches[i].children.length === 1
)
} }
} }
} }
if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) { if (!hasRuntimeConstant && hasHoistedNode && context.transformHoist) {
context.transformHoist(children, context) context.transformHoist(children, context, node)
} }
} }

View File

@ -250,7 +250,7 @@ describe('stringify static html', () => {
}) })
}) })
test('should bail on break content with innerHTML (eg.tables related tags)', () => { test('should bail on tags that has placement constraints (eg.tables related tags)', () => {
const { ast } = compileWithStringify( const { ast } = compileWithStringify(
`<table><tbody>${repeat( `<table><tbody>${repeat(
`<tr class="foo"><td>foo</td></tr>`, `<tr class="foo"><td>foo</td></tr>`,
@ -262,4 +262,36 @@ describe('stringify static html', () => {
type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
}) })
}) })
test('should bail inside slots', () => {
const { ast } = compileWithStringify(
`<foo>${repeat(
`<div class="foo"></div>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</foo>`
)
expect(ast.hoists.length).toBe(
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)
ast.hoists.forEach(node => {
expect(node).toMatchObject({
type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
})
})
const { ast: ast2 } = compileWithStringify(
`<foo><template #foo>${repeat(
`<div class="foo"></div>`,
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)}</template></foo>`
)
expect(ast2.hoists.length).toBe(
StringifyThresholds.ELEMENT_WITH_BINDING_COUNT
)
ast2.hoists.forEach(node => {
expect(node).toMatchObject({
type: NodeTypes.VNODE_CALL // not CALL_EXPRESSION
})
})
})
}) })

View File

@ -59,7 +59,15 @@ type StringifiableNode = PlainElementNode | TextCallNode
* *
* This optimization is only performed in Node.js. * This optimization is only performed in Node.js.
*/ */
export const stringifyStatic: HoistTransform = (children, context) => { export const stringifyStatic: HoistTransform = (children, context, parent) => {
if (
parent.type === NodeTypes.ELEMENT &&
(parent.tagType === ElementTypes.COMPONENT ||
parent.tagType === ElementTypes.TEMPLATE)
) {
return
}
let nc = 0 // current node count let nc = 0 // current node count
let ec = 0 // current element with binding count let ec = 0 // current element with binding count
const currentChunk: StringifiableNode[] = [] const currentChunk: StringifiableNode[] = []