Skip to content

Commit ef6986f

Browse files
authored
refactor(compiler-vapor): move operation with constant values out of renderEffect (#12547)
1 parent bd13001 commit ef6986f

File tree

7 files changed

+182
-71
lines changed

7 files changed

+182
-71
lines changed

packages/compiler-core/src/babelUtils.ts

+76
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,79 @@ export function unwrapTSNode(node: Node): Node {
510510
return node
511511
}
512512
}
513+
514+
export function isStaticNode(node: Node): boolean {
515+
node = unwrapTSNode(node)
516+
517+
switch (node.type) {
518+
case 'UnaryExpression': // void 0, !true
519+
return isStaticNode(node.argument)
520+
521+
case 'LogicalExpression': // 1 > 2
522+
case 'BinaryExpression': // 1 + 2
523+
return isStaticNode(node.left) && isStaticNode(node.right)
524+
525+
case 'ConditionalExpression': {
526+
// 1 ? 2 : 3
527+
return (
528+
isStaticNode(node.test) &&
529+
isStaticNode(node.consequent) &&
530+
isStaticNode(node.alternate)
531+
)
532+
}
533+
534+
case 'SequenceExpression': // (1, 2)
535+
case 'TemplateLiteral': // `foo${1}`
536+
return node.expressions.every(expr => isStaticNode(expr))
537+
538+
case 'ParenthesizedExpression': // (1)
539+
return isStaticNode(node.expression)
540+
541+
case 'StringLiteral':
542+
case 'NumericLiteral':
543+
case 'BooleanLiteral':
544+
case 'NullLiteral':
545+
case 'BigIntLiteral':
546+
return true
547+
}
548+
return false
549+
}
550+
551+
export function isConstantNode(
552+
node: Node,
553+
onIdentifier: (name: string) => boolean,
554+
): boolean {
555+
if (isStaticNode(node)) return true
556+
557+
node = unwrapTSNode(node)
558+
switch (node.type) {
559+
case 'Identifier':
560+
return onIdentifier(node.name)
561+
case 'RegExpLiteral':
562+
return true
563+
case 'ObjectExpression':
564+
return node.properties.every(prop => {
565+
// { bar() {} } object methods are not considered static nodes
566+
if (prop.type === 'ObjectMethod') return false
567+
// { ...{ foo: 1 } }
568+
if (prop.type === 'SpreadElement')
569+
return isConstantNode(prop.argument, onIdentifier)
570+
// { foo: 1 }
571+
return (
572+
(!prop.computed || isConstantNode(prop.key, onIdentifier)) &&
573+
isConstantNode(prop.value, onIdentifier)
574+
)
575+
})
576+
case 'ArrayExpression':
577+
return node.elements.every(element => {
578+
// [1, , 3]
579+
if (element === null) return true
580+
// [1, ...[2, 3]]
581+
if (element.type === 'SpreadElement')
582+
return isConstantNode(element.argument, onIdentifier)
583+
// [1, 2]
584+
return isConstantNode(element, onIdentifier)
585+
})
586+
}
587+
return false
588+
}

packages/compiler-sfc/src/compileScript.ts

+1-37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
BindingTypes,
33
UNREF,
44
isFunctionType,
5+
isStaticNode,
56
unwrapTSNode,
67
walkIdentifiers,
78
} from '@vue/compiler-dom'
@@ -1266,40 +1267,3 @@ function canNeverBeRef(node: Node, userReactiveImport?: string): boolean {
12661267
return false
12671268
}
12681269
}
1269-
1270-
function isStaticNode(node: Node): boolean {
1271-
node = unwrapTSNode(node)
1272-
1273-
switch (node.type) {
1274-
case 'UnaryExpression': // void 0, !true
1275-
return isStaticNode(node.argument)
1276-
1277-
case 'LogicalExpression': // 1 > 2
1278-
case 'BinaryExpression': // 1 + 2
1279-
return isStaticNode(node.left) && isStaticNode(node.right)
1280-
1281-
case 'ConditionalExpression': {
1282-
// 1 ? 2 : 3
1283-
return (
1284-
isStaticNode(node.test) &&
1285-
isStaticNode(node.consequent) &&
1286-
isStaticNode(node.alternate)
1287-
)
1288-
}
1289-
1290-
case 'SequenceExpression': // (1, 2)
1291-
case 'TemplateLiteral': // `foo${1}`
1292-
return node.expressions.every(expr => isStaticNode(expr))
1293-
1294-
case 'ParenthesizedExpression': // (1)
1295-
return isStaticNode(node.expression)
1296-
1297-
case 'StringLiteral':
1298-
case 'NumericLiteral':
1299-
case 'BooleanLiteral':
1300-
case 'NullLiteral':
1301-
case 'BigIntLiteral':
1302-
return true
1303-
}
1304-
return false
1305-
}

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,12 @@ export function render(_ctx) {
317317
`;
318318
319319
exports[`compiler: element transform > props merging: style 1`] = `
320-
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
320+
"import { setStyle as _setStyle, template as _template } from 'vue';
321321
const t0 = _template("<div></div>", true)
322322
323323
export function render(_ctx) {
324324
const n0 = t0()
325-
_renderEffect(() => _setStyle(n0, ["color: green", { color: 'red' }]))
325+
_setStyle(n0, ["color: green", { color: 'red' }])
326326
return n0
327327
}"
328328
`;

packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap

+26
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,29 @@ export function render(_ctx) {
400400
return n0
401401
}"
402402
`;
403+
404+
exports[`compiler v-bind > with constant value 1`] = `
405+
"import { setProp as _setProp, template as _template } from 'vue';
406+
const t0 = _template("<div f=\\"foo1\\" h=\\"1\\"></div>", true)
407+
408+
export function render(_ctx, $props, $emit, $attrs, $slots) {
409+
const n0 = t0()
410+
_setProp(n0, "a", void 0)
411+
_setProp(n0, "b", 1 > 2)
412+
_setProp(n0, "c", 1 + 2)
413+
_setProp(n0, "d", 1 ? 2 : 3)
414+
_setProp(n0, "e", (2))
415+
_setProp(n0, "g", 1)
416+
_setProp(n0, "i", true)
417+
_setProp(n0, "j", null)
418+
_setProp(n0, "k", _ctx.x)
419+
_setProp(n0, "l", { foo: 1 })
420+
_setProp(n0, "m", { [_ctx.x]: 1 })
421+
_setProp(n0, "n", { ...{ foo: 1 } })
422+
_setProp(n0, "o", [1, , 3])
423+
_setProp(n0, "p", [1, ...[2, 3]])
424+
_setProp(n0, "q", [1, 2])
425+
_setProp(n0, "r", /\\s+/)
426+
return n0
427+
}"
428+
`;

packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts

+19-30
Original file line numberDiff line numberDiff line change
@@ -720,40 +720,29 @@ describe('compiler: element transform', () => {
720720
)
721721
expect(code).toMatchSnapshot()
722722

723-
expect(ir.block.effect).toMatchObject([
723+
expect(ir.block.operation).toMatchObject([
724724
{
725-
expressions: [
726-
{
725+
type: IRNodeTypes.SET_PROP,
726+
element: 0,
727+
prop: {
728+
key: {
727729
type: NodeTypes.SIMPLE_EXPRESSION,
728-
content: `{ color: 'red' }`,
729-
isStatic: false,
730+
content: 'style',
731+
isStatic: true,
730732
},
731-
],
732-
operations: [
733-
{
734-
type: IRNodeTypes.SET_PROP,
735-
element: 0,
736-
prop: {
737-
key: {
738-
type: NodeTypes.SIMPLE_EXPRESSION,
739-
content: 'style',
740-
isStatic: true,
741-
},
742-
values: [
743-
{
744-
type: NodeTypes.SIMPLE_EXPRESSION,
745-
content: 'color: green',
746-
isStatic: true,
747-
},
748-
{
749-
type: NodeTypes.SIMPLE_EXPRESSION,
750-
content: `{ color: 'red' }`,
751-
isStatic: false,
752-
},
753-
],
733+
values: [
734+
{
735+
type: NodeTypes.SIMPLE_EXPRESSION,
736+
content: 'color: green',
737+
isStatic: true,
754738
},
755-
},
756-
],
739+
{
740+
type: NodeTypes.SIMPLE_EXPRESSION,
741+
content: `{ color: 'red' }`,
742+
isStatic: false,
743+
},
744+
],
745+
},
757746
},
758747
])
759748
})

packages/compiler-vapor/__tests__/transforms/vBind.spec.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
1+
import { BindingTypes, ErrorCodes, NodeTypes } from '@vue/compiler-dom'
22
import {
33
DynamicFlag,
44
IRNodeTypes,
@@ -661,4 +661,36 @@ describe('compiler v-bind', () => {
661661
expect(code).matchSnapshot()
662662
expect(code).contains('{ depth: () => (0) }')
663663
})
664+
665+
test('with constant value', () => {
666+
const { code } = compileWithVBind(
667+
`
668+
<div
669+
:a="void 0"
670+
:b="1 > 2"
671+
:c="1 + 2"
672+
:d="1 ? 2 : 3"
673+
:e="(2)"
674+
:f="\`foo${1}\`"
675+
:g="1"
676+
:h="'1'"
677+
:i="true"
678+
:j="null"
679+
:k="x"
680+
:l="{ foo: 1 }"
681+
:m="{ [x]: 1 }"
682+
:n="{ ...{ foo: 1 } }"
683+
:o="[1, , 3]"
684+
:p="[1, ...[2, 3]]"
685+
:q="[1, 2]"
686+
:r="/\\s+/"
687+
/>`,
688+
{
689+
bindingMetadata: {
690+
x: BindingTypes.LITERAL_CONST,
691+
},
692+
},
693+
)
694+
expect(code).matchSnapshot()
695+
})
664696
})

packages/compiler-vapor/src/transform.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
type AllNode,
33
type TransformOptions as BaseTransformOptions,
4+
BindingTypes,
45
type CommentNode,
56
type CompilerCompatOptions,
67
type ElementNode,
@@ -11,6 +12,7 @@ import {
1112
type TemplateChildNode,
1213
defaultOnError,
1314
defaultOnWarn,
15+
isConstantNode,
1416
isVSlot,
1517
} from '@vue/compiler-dom'
1618
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
@@ -139,7 +141,11 @@ export class TransformContext<T extends AllNode = AllNode> {
139141
...operations: OperationNode[]
140142
): void {
141143
expressions = expressions.filter(exp => !isConstantExpression(exp))
142-
if (this.inVOnce || expressions.length === 0) {
144+
if (
145+
this.inVOnce ||
146+
expressions.length === 0 ||
147+
isStaticExpression(this.root, expressions)
148+
) {
143149
return this.registerOperation(...operations)
144150
}
145151
const existing = this.block.effect.find(e =>
@@ -298,3 +304,21 @@ export function createStructuralDirectiveTransform(
298304
}
299305
}
300306
}
307+
308+
function isStaticExpression(
309+
context: TransformContext,
310+
expressions: SimpleExpressionNode[],
311+
) {
312+
const {
313+
options: { bindingMetadata },
314+
} = context
315+
const isLiteralConst = (name: string) =>
316+
bindingMetadata[name] === BindingTypes.LITERAL_CONST
317+
return expressions.every(node => {
318+
if (node.ast) {
319+
return isConstantNode(node.ast, isLiteralConst)
320+
} else if (node.ast === null) {
321+
return isLiteralConst(node.content)
322+
}
323+
})
324+
}

0 commit comments

Comments
 (0)