]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-vapor): move operation with constant values out of renderEffect...
authoredison <daiwei521@126.com>
Mon, 16 Dec 2024 06:30:38 +0000 (14:30 +0800)
committerGitHub <noreply@github.com>
Mon, 16 Dec 2024 06:30:38 +0000 (14:30 +0800)
packages/compiler-core/src/babelUtils.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/transform.ts

index 056371245fa6c449c788ce002de1e2335625af2e..f81f269157005dfdbccc22d0714461d316c51429 100644 (file)
@@ -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
+}
index 750770e20f45c685f5f42d03e625defc8b424ec4..0d1dc0976f4aa12fc9e5a48a0f32f95bf3ca3185 100644 (file)
@@ -2,6 +2,7 @@ import {
   BindingTypes,
   UNREF,
   isFunctionType,
+  isStaticNode,
   unwrapTSNode,
   walkIdentifiers,
 } from '@vue/compiler-dom'
@@ -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
-}
index efec7b9ccb70925d57941e591dbefecff847b526..1c37dad0f0de1d49b1102eb9ff14efe0f5c0ae63 100644 (file)
@@ -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
 }"
 `;
index 30d5249fd76a6efa99a90f5c20c809bc0717247b..857b3a5d00b44d92a90168d5533202414c5e460a 100644 (file)
@@ -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
+}"
+`;
index b8d6f1166e9321c4cc9aa7f7d04268b3a0592203..030f32eea2b6e4bc0761f8dae82c1d019286d4bf 100644 (file)
@@ -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,
+            },
+          ],
+        },
       },
     ])
   })
index 292b05d40c6dda13c9c186271d6f1e7642fc54a8..d785adfe810e439863f09d5d1abf21a18183707f 100644 (file)
@@ -1,4 +1,4 @@
-import { ErrorCodes, NodeTypes } from '@vue/compiler-dom'
+import { BindingTypes, ErrorCodes, NodeTypes } from '@vue/compiler-dom'
 import {
   DynamicFlag,
   IRNodeTypes,
@@ -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()
+  })
 })
index 3855808998a835798f33b023b956eaba523833be..431054a6e43438cb16442717ebb0a6c2e353a47a 100644 (file)
@@ -1,6 +1,7 @@
 import {
   type AllNode,
   type TransformOptions as BaseTransformOptions,
+  BindingTypes,
   type CommentNode,
   type CompilerCompatOptions,
   type ElementNode,
@@ -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'
@@ -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 =>
@@ -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)
+    }
+  })
+}