feat(compiler-core): do not generate TEXT flag if child is constant
This commit is contained in:
parent
6607edab2d
commit
6a75c3463b
@ -190,7 +190,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
|
|||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
const _createVNode = Vue.createVNode
|
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() {
|
return function render() {
|
||||||
with (this) {
|
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`] = `
|
exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = `
|
||||||
"const _Vue = Vue
|
"const _Vue = Vue
|
||||||
|
|
||||||
|
@ -273,6 +273,28 @@ describe('compiler: hoistStatic transform', () => {
|
|||||||
expect(generate(root).code).toMatchSnapshot()
|
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', () => {
|
test('hoist static props for elements with directives', () => {
|
||||||
const { root, args } = transformWithHoist(
|
const { root, args } = transformWithHoist(
|
||||||
`<div><div id="foo" v-foo/></div>`
|
`<div><div id="foo" v-foo/></div>`
|
||||||
@ -521,8 +543,7 @@ describe('compiler: hoistStatic transform', () => {
|
|||||||
isStatic: false,
|
isStatic: false,
|
||||||
isConstant: true
|
isConstant: true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
'1 /* TEXT */'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
@ -15,9 +15,8 @@ import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
|||||||
import { PatchFlags, isString, isSymbol } from '@vue/shared'
|
import { PatchFlags, isString, isSymbol } from '@vue/shared'
|
||||||
import { isSlotOutlet, findProp } from '../utils'
|
import { isSlotOutlet, findProp } from '../utils'
|
||||||
|
|
||||||
function hasDynamicKey(node: ElementNode) {
|
function hasDynamicKeyOrRef(node: ElementNode) {
|
||||||
const keyProp = findProp(node, 'key')
|
return findProp(node, 'key', true) || findProp(node, 'ref', true)
|
||||||
return keyProp && keyProp.type === NodeTypes.DIRECTIVE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hoistStatic(root: RootNode, context: TransformContext) {
|
export function hoistStatic(root: RootNode, context: TransformContext) {
|
||||||
@ -57,7 +56,7 @@ function walk(
|
|||||||
if (
|
if (
|
||||||
!doNotHoistNode &&
|
!doNotHoistNode &&
|
||||||
isStaticNode(child, resultCache) &&
|
isStaticNode(child, resultCache) &&
|
||||||
!hasDynamicKey(child)
|
!hasDynamicKeyOrRef(child)
|
||||||
) {
|
) {
|
||||||
// whole tree is static
|
// whole tree is static
|
||||||
child.codegenNode = context.hoist(child.codegenNode!)
|
child.codegenNode = context.hoist(child.codegenNode!)
|
||||||
@ -70,7 +69,7 @@ function walk(
|
|||||||
(!flag ||
|
(!flag ||
|
||||||
flag === PatchFlags.NEED_PATCH ||
|
flag === PatchFlags.NEED_PATCH ||
|
||||||
flag === PatchFlags.TEXT) &&
|
flag === PatchFlags.TEXT) &&
|
||||||
!hasDynamicKey(child)
|
!hasDynamicKeyOrRef(child)
|
||||||
) {
|
) {
|
||||||
let codegenNode = child.codegenNode as ElementCodegenNode
|
let codegenNode = child.codegenNode as ElementCodegenNode
|
||||||
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
if (codegenNode.callee === APPLY_DIRECTIVES) {
|
||||||
@ -107,9 +106,9 @@ function getPatchFlag(node: PlainElementNode): number | undefined {
|
|||||||
return flag ? parseInt(flag, 10) : undefined
|
return flag ? parseInt(flag, 10) : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStaticNode(
|
export function isStaticNode(
|
||||||
node: TemplateChildNode | SimpleExpressionNode,
|
node: TemplateChildNode | SimpleExpressionNode,
|
||||||
resultCache: Map<TemplateChildNode, boolean>
|
resultCache: Map<TemplateChildNode, boolean> = new Map()
|
||||||
): boolean {
|
): boolean {
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case NodeTypes.ELEMENT:
|
case NodeTypes.ELEMENT:
|
||||||
@ -121,7 +120,7 @@ function isStaticNode(
|
|||||||
return cached
|
return cached
|
||||||
}
|
}
|
||||||
const flag = getPatchFlag(node)
|
const flag = getPatchFlag(node)
|
||||||
if (!flag || flag === PatchFlags.TEXT) {
|
if (!flag) {
|
||||||
// element self is static. check its children.
|
// element self is static. check its children.
|
||||||
for (let i = 0; i < node.children.length; i++) {
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
if (!isStaticNode(node.children[i], resultCache)) {
|
if (!isStaticNode(node.children[i], resultCache)) {
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
|
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
|
||||||
import { buildSlots } from './vSlot'
|
import { buildSlots } from './vSlot'
|
||||||
|
import { isStaticNode } from './hoistStatic'
|
||||||
|
|
||||||
// some directive transforms (e.g. v-model) may return a symbol for runtime
|
// some directive transforms (e.g. v-model) may return a symbol for runtime
|
||||||
// import, which should be used instead of a resolveDirective call.
|
// 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) {
|
} else if (node.children.length === 1) {
|
||||||
const child = node.children[0]
|
const child = node.children[0]
|
||||||
const type = child.type
|
const type = child.type
|
||||||
|
// check for dynamic text children
|
||||||
const hasDynamicTextChild =
|
const hasDynamicTextChild =
|
||||||
type === NodeTypes.INTERPOLATION ||
|
type === NodeTypes.INTERPOLATION ||
|
||||||
type === NodeTypes.COMPOUND_EXPRESSION
|
type === NodeTypes.COMPOUND_EXPRESSION
|
||||||
if (hasDynamicTextChild) {
|
if (hasDynamicTextChild && !isStaticNode(child)) {
|
||||||
patchFlag |= PatchFlags.TEXT
|
patchFlag |= PatchFlags.TEXT
|
||||||
}
|
}
|
||||||
// pass directly if the only child is a text node
|
// pass directly if the only child is a text node
|
||||||
|
@ -156,15 +156,18 @@ export function findDir(
|
|||||||
|
|
||||||
export function findProp(
|
export function findProp(
|
||||||
node: ElementNode,
|
node: ElementNode,
|
||||||
name: string
|
name: string,
|
||||||
|
dynamicOnly: boolean = false
|
||||||
): ElementNode['props'][0] | undefined {
|
): ElementNode['props'][0] | undefined {
|
||||||
for (let i = 0; i < node.props.length; i++) {
|
for (let i = 0; i < node.props.length; i++) {
|
||||||
const p = node.props[i]
|
const p = node.props[i]
|
||||||
if (p.type === NodeTypes.ATTRIBUTE) {
|
if (p.type === NodeTypes.ATTRIBUTE) {
|
||||||
|
if (dynamicOnly) continue
|
||||||
if (p.name === name && p.value && !p.value.isEmpty) {
|
if (p.name === name && p.value && !p.value.isEmpty) {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
p.name === 'bind' &&
|
||||||
p.arg &&
|
p.arg &&
|
||||||
p.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
|
p.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
|
||||||
p.arg.isStatic &&
|
p.arg.isStatic &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user