refactor(compiler-core): simplify hoistStatic check for nodes without patchFlag

close #1912
This commit is contained in:
Evan You 2020-08-20 11:43:34 -04:00
parent 255a2bd178
commit 91700fbec2
4 changed files with 42 additions and 24 deletions

View File

@ -271,6 +271,22 @@ return function render(_ctx, _cache) {
}" }"
`; `;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist keyed template v-for with plain element child 1`] = `
"const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
(_openBlock(true), _createBlock(_Fragment, null, _renderList(items, (item) => {
return (_openBlock(), _createBlock(\\"span\\", { key: item }))
}), 128 /* KEYED_FRAGMENT */))
]))
}
}"
`;
exports[`compiler: hoistStatic transform should NOT hoist components 1`] = ` exports[`compiler: hoistStatic transform should NOT hoist components 1`] = `
"const _Vue = Vue "const _Vue = Vue

View File

@ -600,5 +600,13 @@ describe('compiler: hoistStatic transform', () => {
}).code }).code
).toMatchSnapshot() ).toMatchSnapshot()
}) })
test('should NOT hoist keyed template v-for with plain element child', () => {
const root = transformWithHoist(
`<div><template v-for="item in items" :key="item"><span/></template></div>`
)
expect(root.hoists.length).toBe(0)
expect(generate(root).code).toMatchSnapshot()
})
}) })
}) })

View File

@ -7,13 +7,12 @@ import {
PlainElementNode, PlainElementNode,
ComponentNode, ComponentNode,
TemplateNode, TemplateNode,
ElementNode,
VNodeCall, VNodeCall,
ParentNode 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'
import { isSlotOutlet, findProp } from '../utils' import { isSlotOutlet } from '../utils'
export function hoistStatic(root: RootNode, context: TransformContext) { export function hoistStatic(root: RootNode, context: TransformContext) {
walk( walk(
@ -93,8 +92,7 @@ function walk(
(!flag || (!flag ||
flag === PatchFlags.NEED_PATCH || flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) && flag === PatchFlags.TEXT) &&
!hasDynamicKeyOrRef(child) && !hasNonHoistableProps(child)
!hasCachedProps(child)
) { ) {
const props = getNodeProps(child) const props = getNodeProps(child)
if (props) { if (props) {
@ -156,7 +154,7 @@ export function getStaticType(
return StaticType.NOT_STATIC return StaticType.NOT_STATIC
} }
const flag = getPatchFlag(codegenNode) const flag = getPatchFlag(codegenNode)
if (!flag && !hasDynamicKeyOrRef(node) && !hasCachedProps(node)) { if (!flag && !hasNonHoistableProps(node)) {
// element self is static. check its children. // element self is static. check its children.
let returnType = StaticType.FULL_STATIC let returnType = StaticType.FULL_STATIC
for (let i = 0; i < node.children.length; i++) { for (let i = 0; i < node.children.length; i++) {
@ -238,28 +236,23 @@ export function getStaticType(
} }
} }
function hasDynamicKeyOrRef(node: ElementNode): boolean { /**
return !!(findProp(node, 'key', true) || findProp(node, 'ref', true)) * Even for a node with no patch flag, it is possible for it to contain
} * non-hoistable expressions that refers to scope variables, e.g. compiler
* injected keys or cached event handlers. Therefore we need to always check the
function hasCachedProps(node: PlainElementNode): boolean { * codegenNode's props to be sure.
if (__BROWSER__) { */
return false function hasNonHoistableProps(node: PlainElementNode): boolean {
}
const props = getNodeProps(node) const props = getNodeProps(node)
if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) { if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
const { properties } = props const { properties } = props
for (let i = 0; i < properties.length; i++) { for (let i = 0; i < properties.length; i++) {
const val = properties[i].value const { key, value } = properties[i]
if (val.type === NodeTypes.JS_CACHE_EXPRESSION) {
return true
}
// merged event handlers
if ( if (
val.type === NodeTypes.JS_ARRAY_EXPRESSION && key.type !== NodeTypes.SIMPLE_EXPRESSION ||
val.elements.some( !key.isStatic ||
e => !isString(e) && e.type === NodeTypes.JS_CACHE_EXPRESSION (value.type !== NodeTypes.SIMPLE_EXPRESSION ||
) (!value.isStatic && !value.isConstant))
) { ) {
return true return true
} }

View File

@ -19,7 +19,8 @@ import {
BlockCodegenNode, BlockCodegenNode,
IfNode, IfNode,
createVNodeCall, createVNodeCall,
AttributeNode AttributeNode,
locStub
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
@ -222,7 +223,7 @@ function createChildrenCodegenNode(
const { helper } = context const { helper } = context
const keyProperty = createObjectProperty( const keyProperty = createObjectProperty(
`key`, `key`,
createSimpleExpression(`${keyIndex}`, false) createSimpleExpression(`${keyIndex}`, false, locStub, true)
) )
const { children } = branch const { children } = branch
const firstChild = children[0] const firstChild = children[0]