Skip to content

Commit 6a75c34

Browse files
committed
feat(compiler-core): do not generate TEXT flag if child is constant
1 parent 6607eda commit 6a75c34

File tree

5 files changed

+52
-13
lines changed

5 files changed

+52
-13
lines changed

packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
190190
"const _Vue = Vue
191191
const _createVNode = Vue.createVNode
192192
193-
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1), 1 /* TEXT */)
193+
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1))
194194
195195
return function render() {
196196
with (this) {
@@ -300,6 +300,20 @@ return function render() {
300300
}"
301301
`;
302302
303+
exports[`compiler: hoistStatic transform should NOT hoist element with dynamic ref 1`] = `
304+
"const _Vue = Vue
305+
306+
return function render() {
307+
with (this) {
308+
const { createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
309+
310+
return (_openBlock(), _createBlock(\\"div\\", null, [
311+
_createVNode(\\"div\\", { ref: foo }, null, 32 /* NEED_PATCH */)
312+
]))
313+
}
314+
}"
315+
`;
316+
303317
exports[`compiler: hoistStatic transform should NOT hoist root node 1`] = `
304318
"const _Vue = Vue
305319

packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,28 @@ describe('compiler: hoistStatic transform', () => {
273273
expect(generate(root).code).toMatchSnapshot()
274274
})
275275

276+
test('should NOT hoist element with dynamic ref', () => {
277+
const { root, args } = transformWithHoist(`<div><div :ref="foo"/></div>`)
278+
expect(root.hoists.length).toBe(0)
279+
expect(args[2]).toMatchObject([
280+
{
281+
type: NodeTypes.ELEMENT,
282+
codegenNode: {
283+
callee: CREATE_VNODE,
284+
arguments: [
285+
`"div"`,
286+
createObjectMatcher({
287+
ref: `[foo]`
288+
}),
289+
`null`,
290+
genFlagText(PatchFlags.NEED_PATCH)
291+
]
292+
}
293+
}
294+
])
295+
expect(generate(root).code).toMatchSnapshot()
296+
})
297+
276298
test('hoist static props for elements with directives', () => {
277299
const { root, args } = transformWithHoist(
278300
`<div><div id="foo" v-foo/></div>`
@@ -521,8 +543,7 @@ describe('compiler: hoistStatic transform', () => {
521543
isStatic: false,
522544
isConstant: true
523545
}
524-
},
525-
'1 /* TEXT */'
546+
}
526547
]
527548
}
528549
])

packages/compiler-core/src/transforms/hoistStatic.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import { APPLY_DIRECTIVES } from '../runtimeHelpers'
1515
import { PatchFlags, isString, isSymbol } from '@vue/shared'
1616
import { isSlotOutlet, findProp } from '../utils'
1717

18-
function hasDynamicKey(node: ElementNode) {
19-
const keyProp = findProp(node, 'key')
20-
return keyProp && keyProp.type === NodeTypes.DIRECTIVE
18+
function hasDynamicKeyOrRef(node: ElementNode) {
19+
return findProp(node, 'key', true) || findProp(node, 'ref', true)
2120
}
2221

2322
export function hoistStatic(root: RootNode, context: TransformContext) {
@@ -57,7 +56,7 @@ function walk(
5756
if (
5857
!doNotHoistNode &&
5958
isStaticNode(child, resultCache) &&
60-
!hasDynamicKey(child)
59+
!hasDynamicKeyOrRef(child)
6160
) {
6261
// whole tree is static
6362
child.codegenNode = context.hoist(child.codegenNode!)
@@ -70,7 +69,7 @@ function walk(
7069
(!flag ||
7170
flag === PatchFlags.NEED_PATCH ||
7271
flag === PatchFlags.TEXT) &&
73-
!hasDynamicKey(child)
72+
!hasDynamicKeyOrRef(child)
7473
) {
7574
let codegenNode = child.codegenNode as ElementCodegenNode
7675
if (codegenNode.callee === APPLY_DIRECTIVES) {
@@ -107,9 +106,9 @@ function getPatchFlag(node: PlainElementNode): number | undefined {
107106
return flag ? parseInt(flag, 10) : undefined
108107
}
109108

110-
function isStaticNode(
109+
export function isStaticNode(
111110
node: TemplateChildNode | SimpleExpressionNode,
112-
resultCache: Map<TemplateChildNode, boolean>
111+
resultCache: Map<TemplateChildNode, boolean> = new Map()
113112
): boolean {
114113
switch (node.type) {
115114
case NodeTypes.ELEMENT:
@@ -121,7 +120,7 @@ function isStaticNode(
121120
return cached
122121
}
123122
const flag = getPatchFlag(node)
124-
if (!flag || flag === PatchFlags.TEXT) {
123+
if (!flag) {
125124
// element self is static. check its children.
126125
for (let i = 0; i < node.children.length; i++) {
127126
if (!isStaticNode(node.children[i], resultCache)) {

packages/compiler-core/src/transforms/transformElement.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
} from '../runtimeHelpers'
2929
import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
3030
import { buildSlots } from './vSlot'
31+
import { isStaticNode } from './hoistStatic'
3132

3233
// some directive transforms (e.g. v-model) may return a symbol for runtime
3334
// import, which should be used instead of a resolveDirective call.
@@ -93,10 +94,11 @@ export const transformElement: NodeTransform = (node, context) => {
9394
} else if (node.children.length === 1) {
9495
const child = node.children[0]
9596
const type = child.type
97+
// check for dynamic text children
9698
const hasDynamicTextChild =
9799
type === NodeTypes.INTERPOLATION ||
98100
type === NodeTypes.COMPOUND_EXPRESSION
99-
if (hasDynamicTextChild) {
101+
if (hasDynamicTextChild && !isStaticNode(child)) {
100102
patchFlag |= PatchFlags.TEXT
101103
}
102104
// pass directly if the only child is a text node

packages/compiler-core/src/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,18 @@ export function findDir(
156156

157157
export function findProp(
158158
node: ElementNode,
159-
name: string
159+
name: string,
160+
dynamicOnly: boolean = false
160161
): ElementNode['props'][0] | undefined {
161162
for (let i = 0; i < node.props.length; i++) {
162163
const p = node.props[i]
163164
if (p.type === NodeTypes.ATTRIBUTE) {
165+
if (dynamicOnly) continue
164166
if (p.name === name && p.value && !p.value.isEmpty) {
165167
return p
166168
}
167169
} else if (
170+
p.name === 'bind' &&
168171
p.arg &&
169172
p.arg.type === NodeTypes.SIMPLE_EXPRESSION &&
170173
p.arg.isStatic &&

0 commit comments

Comments
 (0)