]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: generate specific function when the prop key is static (#97)
authorygj6 <7699524+ygj6@users.noreply.github.com>
Mon, 22 Jan 2024 15:03:39 +0000 (23:03 +0800)
committerGitHub <noreply@github.com>
Mon, 22 Jan 2024 15:03:39 +0000 (23:03 +0800)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
README.md
packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
packages/compiler-vapor/src/generators/prop.ts
packages/compiler-vapor/src/ir.ts
packages/compiler-vapor/src/transforms/vBind.ts

index 9a546d189c5dd4c1a21a06047ead26289cdc79ce..8ff103f92cdf67e83c463937cb808bdf9e5eb8b4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -29,10 +29,10 @@ PR are welcome!
   - [x] `v-bind`
     - [x] simple expression
     - [x] compound expression
-    - [ ] modifiers
+    - [x] modifiers
       - [x] .camel
-      - [ ] .prop
-      - [ ] .attr
+      - [x] .prop
+      - [x] .attr
   - [x] `v-on`
     - [x] simple expression
     - [x] compound expression
index 0f5e5203cd8e9ed156d518d736b13539aa73de95..1484e4ea74312aea8e025a672b7ed3c00a3c58b7 100644 (file)
@@ -1,28 +1,28 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler v-bind > .attr modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)
+    _setAttr(n1, "foo-bar", undefined, _ctx.id)
   })
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)
+    _setAttr(n1, "foo-bar", undefined, _ctx.fooBar)
   })
   return n0
 }"
@@ -71,42 +71,42 @@ export function render(_ctx) {
 `;
 
 exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+    _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
   })
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+    _setDOMProp(n1, "fooBar", undefined, _ctx.id)
   })
   return n0
 }"
 `;
 
 exports[`compiler v-bind > .prop modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+    _setDOMProp(n1, "fooBar", undefined, _ctx.id)
   })
   return n0
 }"
@@ -127,14 +127,14 @@ export function render(_ctx) {
 `;
 
 exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div></div>")
   const n0 = t0()
   const { 0: [n1],} = _children(n0)
   _renderEffect(() => {
-    _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+    _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
   })
   return n0
 }"
index 7a761ed3c66bce7242a5a6d8d322b594b5a83b17..4fb9d979d51c24aff5f2378358206e96c29e97b2 100644 (file)
@@ -13,7 +13,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: v-once > basic 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend } from 'vue/vapor';
 
 export function render(_ctx) {
   const t0 = _template("<div> <span></span></div>")
@@ -21,7 +21,7 @@ export function render(_ctx) {
   const { 0: [n3, { 1: [n2],}],} = _children(n0)
   const n1 = _createTextNode(_ctx.msg)
   _setText(n1, undefined, _ctx.msg)
-  _setDynamicProp(n2, "class", undefined, _ctx.clz)
+  _setClass(n2, "class", undefined, _ctx.clz)
   _prepend(n3, n1)
   return n0
 }"
index de35a24094d033e05726b8c0a5c1a93c4af54ab2..b8f5d335ddd844dbb3cece7f495b2ab0b95100e3 100644 (file)
@@ -189,6 +189,8 @@ describe('compiler v-bind', () => {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: undefined,
     })
 
     expect(code).matchSnapshot()
@@ -207,6 +209,8 @@ describe('compiler v-bind', () => {
         content: `fooBar`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: undefined,
     })
 
     expect(code).matchSnapshot()
@@ -220,7 +224,6 @@ describe('compiler v-bind', () => {
     const { ir, code } = compileWithVBind(`<div v-bind:[foo].camel="id"/>`)
 
     expect(ir.effect[0].operations[0]).toMatchObject({
-      runtimeCamelize: true,
       key: {
         content: `foo`,
         isStatic: false,
@@ -229,6 +232,8 @@ describe('compiler v-bind', () => {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: true,
+      modifier: undefined,
     })
 
     expect(code).matchSnapshot()
@@ -245,18 +250,20 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `.fooBar`,
+        content: `fooBar`,
         isStatic: true,
       },
       value: {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '.',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+    expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
   })
 
   test('.prop modifier w/ no expression', () => {
@@ -264,20 +271,20 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `.fooBar`,
+        content: `fooBar`,
         isStatic: true,
       },
       value: {
         content: `fooBar`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '.',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains(
-      '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
-    )
+    expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
   })
 
   test('.prop modifier w/ dynamic arg', () => {
@@ -292,6 +299,8 @@ describe('compiler v-bind', () => {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '.',
     })
 
     expect(code).matchSnapshot()
@@ -308,18 +317,20 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `.fooBar`,
+        content: `fooBar`,
         isStatic: true,
       },
       value: {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '.',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+    expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
   })
 
   test('.prop modifier (shortband) w/ no expression', () => {
@@ -327,20 +338,20 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `.fooBar`,
+        content: `fooBar`,
         isStatic: true,
       },
       value: {
         content: `fooBar`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '.',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains(
-      '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
-    )
+    expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
   })
 
   test('.attr modifier', () => {
@@ -348,18 +359,20 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `^foo-bar`,
+        content: `foo-bar`,
         isStatic: true,
       },
       value: {
         content: `id`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '^',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)')
+    expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.id)')
   })
 
   test('.attr modifier w/ no expression', () => {
@@ -367,19 +380,19 @@ describe('compiler v-bind', () => {
 
     expect(ir.effect[0].operations[0]).toMatchObject({
       key: {
-        content: `^foo-bar`,
+        content: `foo-bar`,
         isStatic: true,
       },
       value: {
         content: `fooBar`,
         isStatic: false,
       },
+      runtimeCamelize: false,
+      modifier: '^',
     })
 
     expect(code).matchSnapshot()
     expect(code).contains('renderEffect')
-    expect(code).contains(
-      '_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)',
-    )
+    expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.fooBar)')
   })
 })
index 89e0fc6df0fbcd83ee89616953766f0440dc3ccd..4dd4a1c95894a295ab2abf69dd41db9a3f3f9994 100644 (file)
@@ -1,20 +1,56 @@
 import type { CodegenContext } from '../generate'
 import type { SetPropIRNode } from '../ir'
 import { genExpression } from './expression'
+import { isString } from '@vue/shared'
 
 export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
   const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
 
   newline()
+
+  const element = `n${oper.element}`
+
+  // fast path for static props
+  if (isString(oper.key) || oper.key.isStatic) {
+    const keyName = isString(oper.key) ? oper.key : oper.key.content
+
+    let helperName: string | undefined
+    if (keyName === 'class') {
+      helperName = 'setClass'
+    } else if (keyName === 'style') {
+      helperName = 'setStyle'
+    } else if (oper.modifier) {
+      helperName = oper.modifier === '.' ? 'setDOMProp' : 'setAttr'
+    }
+
+    if (helperName) {
+      pushFnCall(
+        vaporHelper(helperName),
+        element,
+        () => {
+          const expr = () => genExpression(oper.key, context)
+          if (oper.runtimeCamelize) {
+            pushFnCall(helper('camelize'), expr)
+          } else {
+            expr()
+          }
+        },
+        'undefined',
+        () => genExpression(oper.value, context),
+      )
+      return
+    }
+  }
+
   pushFnCall(
     vaporHelper('setDynamicProp'),
-    `n${oper.element}`,
+    element,
     // 2. key name
     () => {
       if (oper.runtimeCamelize) {
         pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
-      } else if (oper.runtimePrefix) {
-        pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () =>
+      } else if (oper.modifier) {
+        pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
           genExpression(oper.key, context),
         )
       } else {
index 00fcdb587588562b4c5f8fae7137243e94fa7ba0..1c2f6ee308a65a79d143ef681fa983eff80059f1 100644 (file)
@@ -63,8 +63,8 @@ export interface SetPropIRNode extends BaseIRNode {
   element: number
   key: IRExpression
   value: IRExpression
+  modifier?: '.' | '^'
   runtimeCamelize: boolean
-  runtimePrefix?: string
 }
 
 export interface SetTextIRNode extends BaseIRNode {
index 1379db91c23434432414b4c71b7cbd60fe184b08..3443247219bad4cbd39302ab2267d7c7e5980ad7 100644 (file)
@@ -38,14 +38,6 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
     }
   }
 
-  let prefix: string | undefined
-  if (modifiers.includes('prop')) {
-    prefix = injectPrefix(arg, '.')
-  }
-  if (modifiers.includes('attr')) {
-    prefix = injectPrefix(arg, '^')
-  }
-
   if (!exp.content.trim()) {
     context.options.onError(
       createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
@@ -64,15 +56,12 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
         key: arg,
         value: exp,
         runtimeCamelize: camel,
-        runtimePrefix: prefix,
+        modifier: modifiers.includes('prop')
+          ? '.'
+          : modifiers.includes('attr')
+            ? '^'
+            : undefined,
       },
     ],
   )
 }
-
-const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => {
-  if (!arg.isStatic) {
-    return prefix
-  }
-  arg.content = prefix + arg.content
-}