feat(compiler-core): do not generate TEXT flag if child is constant

This commit is contained in:
Evan You 2019-10-16 12:00:55 -04:00
parent 6607edab2d
commit 6a75c3463b
5 changed files with 52 additions and 13 deletions

View File

@ -190,7 +190,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
"const _Vue = Vue
const _createVNode = Vue.createVNode
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1), 1 /* TEXT */)
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1))
return function render() {
with (this) {
@ -300,6 +300,20 @@ return function render() {
}"
`;
exports[`compiler: hoistStatic transform should NOT hoist element with dynamic ref 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
return (_openBlock(), _createBlock(\\"div\\", null, [
_createVNode(\\"div\\", { ref: foo }, null, 32 /* NEED_PATCH */)
]))
}
}"
`;
exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = `
"const _Vue = Vue

View File

@ -273,6 +273,28 @@ describe('compiler: hoistStatic transform', () => {
expect(generate(root).code).toMatchSnapshot()
})
test('should NOT hoist element with dynamic ref', () => {
const { root, args } = transformWithHoist(`<div><div :ref="foo"/></div>`)
expect(root.hoists.length).toBe(0)
expect(args[2]).toMatchObject([
{
type: NodeTypes.ELEMENT,
codegenNode: {
callee: CREATE_VNODE,
arguments: [
`"div"`,
createObjectMatcher({
ref: `[foo]`
}),
`null`,
genFlagText(PatchFlags.NEED_PATCH)
]
}
}
])
expect(generate(root).code).toMatchSnapshot()
})
test('hoist static props for elements with directives', () => {
const { root, args } = transformWithHoist(
`<div><div id="foo" v-foo/></div>`
@ -521,8 +543,7 @@ describe('compiler: hoistStatic transform', () => {
isStatic: false,
isConstant: true
}
},
'1 /* TEXT */'
}
]
}
])

View File

@ -15,9 +15,8 @@ import { APPLY_DIRECTIVES } from '../runtimeHelpers'
import { PatchFlags, isString, isSymbol } from '@vue/shared'
import { isSlotOutlet, findProp } from '../utils'
function hasDynamicKey(node: ElementNode) {
const keyProp = findProp(node, 'key')
return keyProp && keyProp.type === NodeTypes.DIRECTIVE
function hasDynamicKeyOrRef(node: ElementNode) {
return findProp(node, 'key', true) || findProp(node, 'ref', true)
}
export function hoistStatic(root: RootNode, context: TransformContext) {
@ -57,7 +56,7 @@ function walk(
if (
!doNotHoistNode &&
isStaticNode(child, resultCache) &&
!hasDynamicKey(child)
!hasDynamicKeyOrRef(child)
) {
// whole tree is static
child.codegenNode = context.hoist(child.codegenNode!)
@ -70,7 +69,7 @@ function walk(
(!flag ||
flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) &&
!hasDynamicKey(child)
!hasDynamicKeyOrRef(child)
) {
let codegenNode = child.codegenNode as ElementCodegenNode
if (codegenNode.callee === APPLY_DIRECTIVES) {
@ -107,9 +106,9 @@ function getPatchFlag(node: PlainElementNode): number | undefined {
return flag ? parseInt(flag, 10) : undefined
}
function isStaticNode(
export function isStaticNode(
node: TemplateChildNode | SimpleExpressionNode,
resultCache: Map<TemplateChildNode, boolean>
resultCache: Map<TemplateChildNode, boolean> = new Map()
): boolean {
switch (node.type) {
case NodeTypes.ELEMENT:
@ -121,7 +120,7 @@ function isStaticNode(
return cached
}
const flag = getPatchFlag(node)
if (!flag || flag === PatchFlags.TEXT) {
if (!flag) {
// element self is static. check its children.
for (let i = 0; i < node.children.length; i++) {
if (!isStaticNode(node.children[i], resultCache)) {

View File

@ -28,6 +28,7 @@ import {
} from '../runtimeHelpers'
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
import { buildSlots } from './vSlot'
import { isStaticNode } from './hoistStatic'
// some directive transforms (e.g. v-model) may return a symbol for runtime
// import, which should be used instead of a resolveDirective call.
@ -93,10 +94,11 @@ export const transformElement: NodeTransform = (node, context) => {
} else if (node.children.length === 1) {
const child = node.children[0]
const type = child.type
// check for dynamic text children
const hasDynamicTextChild =
type === NodeTypes.INTERPOLATION ||
type === NodeTypes.COMPOUND_EXPRESSION
if (hasDynamicTextChild) {
if (hasDynamicTextChild && !isStaticNode(child)) {
patchFlag |= PatchFlags.TEXT
}
// pass directly if the only child is a text node

View File

@ -156,15 +156,18 @@ export function findDir(
export function findProp(
node: ElementNode,
name: string
name: string,
dynamicOnly: boolean = false
): ElementNode['props'][0] | undefined {
for (let i = 0; i < node.props.length; i++) {
const p = node.props[i]
if (p.type === NodeTypes.ATTRIBUTE) {
if (dynamicOnly) continue
if (p.name === name && p.value && !p.value.isEmpty) {
return p
}
} else if (
p.name === 'bind' &&
p.arg &&
p.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
p.arg.isStatic &&