Skip to content

Commit

Permalink
refactor(compiler-vapor): move operation with constant values out of …
Browse files Browse the repository at this point in the history
…renderEffect (#12547)
  • Loading branch information
edison1105 authored Dec 16, 2024
1 parent bd13001 commit ef6986f
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 71 deletions.
76 changes: 76 additions & 0 deletions packages/compiler-core/src/babelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,3 +510,79 @@ export function unwrapTSNode(node: Node): Node {
return node
}
}

export function isStaticNode(node: Node): boolean {
node = unwrapTSNode(node)

switch (node.type) {
case 'UnaryExpression': // void 0, !true
return isStaticNode(node.argument)

case 'LogicalExpression': // 1 > 2
case 'BinaryExpression': // 1 + 2
return isStaticNode(node.left) && isStaticNode(node.right)

case 'ConditionalExpression': {
// 1 ? 2 : 3
return (
isStaticNode(node.test) &&
isStaticNode(node.consequent) &&
isStaticNode(node.alternate)
)
}

case 'SequenceExpression': // (1, 2)
case 'TemplateLiteral': // `foo${1}`
return node.expressions.every(expr => isStaticNode(expr))

case 'ParenthesizedExpression': // (1)
return isStaticNode(node.expression)

case 'StringLiteral':
case 'NumericLiteral':
case 'BooleanLiteral':
case 'NullLiteral':
case 'BigIntLiteral':
return true
}
return false
}

export function isConstantNode(
node: Node,
onIdentifier: (name: string) => boolean,
): boolean {
if (isStaticNode(node)) return true

node = unwrapTSNode(node)
switch (node.type) {
case 'Identifier':
return onIdentifier(node.name)
case 'RegExpLiteral':
return true
case 'ObjectExpression':
return node.properties.every(prop => {
// { bar() {} } object methods are not considered static nodes
if (prop.type === 'ObjectMethod') return false
// { ...{ foo: 1 } }
if (prop.type === 'SpreadElement')
return isConstantNode(prop.argument, onIdentifier)
// { foo: 1 }
return (
(!prop.computed || isConstantNode(prop.key, onIdentifier)) &&
isConstantNode(prop.value, onIdentifier)
)
})
case 'ArrayExpression':
return node.elements.every(element => {
// [1, , 3]
if (element === null) return true
// [1, ...[2, 3]]
if (element.type === 'SpreadElement')
return isConstantNode(element.argument, onIdentifier)
// [1, 2]
return isConstantNode(element, onIdentifier)
})
}
return false
}
38 changes: 1 addition & 37 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BindingTypes,
UNREF,
isFunctionType,
isStaticNode,
unwrapTSNode,
walkIdentifiers,
} from '@vue/compiler-dom'
Expand Down Expand Up @@ -1266,40 +1267,3 @@ function canNeverBeRef(node: Node, userReactiveImport?: string): boolean {
return false
}
}

function isStaticNode(node: Node): boolean {
node = unwrapTSNode(node)

switch (node.type) {
case 'UnaryExpression': // void 0, !true
return isStaticNode(node.argument)

case 'LogicalExpression': // 1 > 2
case 'BinaryExpression': // 1 + 2
return isStaticNode(node.left) && isStaticNode(node.right)

case 'ConditionalExpression': {
// 1 ? 2 : 3
return (
isStaticNode(node.test) &&
isStaticNode(node.consequent) &&
isStaticNode(node.alternate)
)
}

case 'SequenceExpression': // (1, 2)
case 'TemplateLiteral': // `foo${1}`
return node.expressions.every(expr => isStaticNode(expr))

case 'ParenthesizedExpression': // (1)
return isStaticNode(node.expression)

case 'StringLiteral':
case 'NumericLiteral':
case 'BooleanLiteral':
case 'NullLiteral':
case 'BigIntLiteral':
return true
}
return false
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,12 @@ export function render(_ctx) {
`;
exports[`compiler: element transform > props merging: style 1`] = `
"import { setStyle as _setStyle, renderEffect as _renderEffect, template as _template } from 'vue';
"import { setStyle as _setStyle, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
export function render(_ctx) {
const n0 = t0()
_renderEffect(() => _setStyle(n0, ["color: green", { color: 'red' }]))
_setStyle(n0, ["color: green", { color: 'red' }])
return n0
}"
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,29 @@ export function render(_ctx) {
return n0
}"
`;
exports[`compiler v-bind > with constant value 1`] = `
"import { setProp as _setProp, template as _template } from 'vue';
const t0 = _template("<div f=\\"foo1\\" h=\\"1\\"></div>", true)
export function render(_ctx, $props, $emit, $attrs, $slots) {
const n0 = t0()
_setProp(n0, "a", void 0)
_setProp(n0, "b", 1 > 2)
_setProp(n0, "c", 1 + 2)
_setProp(n0, "d", 1 ? 2 : 3)
_setProp(n0, "e", (2))
_setProp(n0, "g", 1)
_setProp(n0, "i", true)
_setProp(n0, "j", null)
_setProp(n0, "k", _ctx.x)
_setProp(n0, "l", { foo: 1 })
_setProp(n0, "m", { [_ctx.x]: 1 })
_setProp(n0, "n", { ...{ foo: 1 } })
_setProp(n0, "o", [1, , 3])
_setProp(n0, "p", [1, ...[2, 3]])
_setProp(n0, "q", [1, 2])
_setProp(n0, "r", /\\s+/)
return n0
}"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -720,40 +720,29 @@ describe('compiler: element transform', () => {
)
expect(code).toMatchSnapshot()

expect(ir.block.effect).toMatchObject([
expect(ir.block.operation).toMatchObject([
{
expressions: [
{
type: IRNodeTypes.SET_PROP,
element: 0,
prop: {
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: `{ color: 'red' }`,
isStatic: false,
content: 'style',
isStatic: true,
},
],
operations: [
{
type: IRNodeTypes.SET_PROP,
element: 0,
prop: {
key: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'style',
isStatic: true,
},
values: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'color: green',
isStatic: true,
},
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `{ color: 'red' }`,
isStatic: false,
},
],
values: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'color: green',
isStatic: true,
},
},
],
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: `{ color: 'red' }`,
isStatic: false,
},
],
},
},
])
})
Expand Down
34 changes: 33 additions & 1 deletion packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
import { BindingTypes, ErrorCodes, NodeTypes } from '@vue/compiler-dom'
import {
DynamicFlag,
IRNodeTypes,
Expand Down Expand Up @@ -661,4 +661,36 @@ describe('compiler v-bind', () => {
expect(code).matchSnapshot()
expect(code).contains('{ depth: () => (0) }')
})

test('with constant value', () => {
const { code } = compileWithVBind(
`
<div
:a="void 0"
:b="1 > 2"
:c="1 + 2"
:d="1 ? 2 : 3"
:e="(2)"
:f="\`foo${1}\`"
:g="1"
:h="'1'"
:i="true"
:j="null"
:k="x"
:l="{ foo: 1 }"
:m="{ [x]: 1 }"
:n="{ ...{ foo: 1 } }"
:o="[1, , 3]"
:p="[1, ...[2, 3]]"
:q="[1, 2]"
:r="/\\s+/"
/>`,
{
bindingMetadata: {
x: BindingTypes.LITERAL_CONST,
},
},
)
expect(code).matchSnapshot()
})
})
26 changes: 25 additions & 1 deletion packages/compiler-vapor/src/transform.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type AllNode,
type TransformOptions as BaseTransformOptions,
BindingTypes,
type CommentNode,
type CompilerCompatOptions,
type ElementNode,
Expand All @@ -11,6 +12,7 @@ import {
type TemplateChildNode,
defaultOnError,
defaultOnWarn,
isConstantNode,
isVSlot,
} from '@vue/compiler-dom'
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
Expand Down Expand Up @@ -139,7 +141,11 @@ export class TransformContext<T extends AllNode = AllNode> {
...operations: OperationNode[]
): void {
expressions = expressions.filter(exp => !isConstantExpression(exp))
if (this.inVOnce || expressions.length === 0) {
if (
this.inVOnce ||
expressions.length === 0 ||
isStaticExpression(this.root, expressions)
) {
return this.registerOperation(...operations)
}
const existing = this.block.effect.find(e =>
Expand Down Expand Up @@ -298,3 +304,21 @@ export function createStructuralDirectiveTransform(
}
}
}

function isStaticExpression(
context: TransformContext,
expressions: SimpleExpressionNode[],
) {
const {
options: { bindingMetadata },
} = context
const isLiteralConst = (name: string) =>
bindingMetadata[name] === BindingTypes.LITERAL_CONST
return expressions.every(node => {
if (node.ast) {
return isConstantNode(node.ast, isLiteralConst)
} else if (node.ast === null) {
return isLiteralConst(node.content)
}
})
}

0 comments on commit ef6986f

Please sign in to comment.