Преглед изворни кода

fix(compiler-vapor): v-on for component support `$event` argument (#177)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
Jevon пре 2 година
родитељ
комит
e640ec6088

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

@@ -91,6 +91,17 @@ export function render(_ctx) {
 }"
 `;
 
+exports[`compiler: element transform > component > should wrap as function if v-on expression is inline statement 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
+
+export function render(_ctx) {
+  const n0 = _createComponent(_resolveComponent("Foo"), [{
+    onBar: () => $event => (_ctx.handleBar($event))
+  }], true)
+  return n0
+}"
+`;
+
 exports[`compiler: element transform > component > static props 1`] = `
 "import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
 

+ 23 - 0
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts

@@ -364,6 +364,29 @@ describe('compiler: element transform', () => {
         },
       ])
     })
+
+    test('should wrap as function if v-on expression is inline statement', () => {
+      const { code, ir } = compileWithElementTransform(
+        `<Foo v-on:bar="handleBar($event)" />`,
+      )
+      expect(code).toMatchSnapshot()
+      expect(code).contains(`onBar: () => $event => (_ctx.handleBar($event))`)
+      expect(ir.block.operation).toMatchObject([
+        {
+          type: IRNodeTypes.CREATE_COMPONENT_NODE,
+          tag: 'Foo',
+          props: [
+            [
+              {
+                key: { content: 'bar' },
+                handler: true,
+                values: [{ content: 'handleBar($event)' }],
+              },
+            ],
+          ],
+        },
+      ])
+    })
   })
 
   test('static props', () => {

+ 5 - 3
packages/compiler-vapor/src/generators/component.ts

@@ -12,6 +12,7 @@ import {
 import { genExpression } from './expression'
 import { genPropKey } from './prop'
 import { createSimpleExpression } from '@vue/compiler-dom'
+import { genEventHandler } from './event'
 
 // TODO: generate component slots
 export function genCreateComponent(
@@ -74,9 +75,10 @@ export function genCreateComponent(
       ...props.map(prop => {
         return [
           ...genPropKey(prop, context),
-          ': () => (',
-          ...genExpression(prop.values[0], context),
-          ')',
+          ': ',
+          ...(prop.handler
+            ? genEventHandler(context, prop.values[0])
+            : ['() => (', ...genExpression(prop.values[0], context), ')']),
         ]
       }),
     )

+ 34 - 27
packages/compiler-vapor/src/generators/event.ts

@@ -1,4 +1,8 @@
-import { fnExpRE, isMemberExpression } from '@vue/compiler-dom'
+import {
+  type SimpleExpressionNode,
+  fnExpRE,
+  isMemberExpression,
+} from '@vue/compiler-dom'
 import type { CodegenContext } from '../generate'
 import type { SetDynamicEventsIRNode, SetEventIRNode } from '../ir'
 import { genExpression } from './expression'
@@ -15,11 +19,11 @@ export function genSetEvent(
   oper: SetEventIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { vaporHelper, options } = context
+  const { vaporHelper } = context
   const { element, key, keyOverride, value, modifiers, delegate, effect } = oper
 
   const name = genName()
-  const handler = genEventHandler()
+  const handler = genEventHandler(context, value)
   const eventOptions = genEventOptions()
 
   if (delegate) {
@@ -51,30 +55,6 @@ export function genSetEvent(
     }
   }
 
-  function genEventHandler() {
-    if (value && value.content.trim()) {
-      const isMemberExp = isMemberExpression(value.content, options)
-      const isInlineStatement = !(isMemberExp || fnExpRE.test(value.content))
-
-      if (isInlineStatement) {
-        const expr = context.withId(() => genExpression(value, context), {
-          $event: null,
-        })
-        const hasMultipleStatements = value.content.includes(`;`)
-        return [
-          '() => $event => ',
-          hasMultipleStatements ? '{' : '(',
-          ...expr,
-          hasMultipleStatements ? '}' : ')',
-        ]
-      } else {
-        return ['() => ', ...genExpression(value, context)]
-      }
-    }
-
-    return ['() => {}']
-  }
-
   function genEventOptions(): CodeFragment[] | undefined {
     let { options, keys, nonKeys } = modifiers
     if (!options.length && !nonKeys.length && !keys.length && !effect) return
@@ -111,3 +91,30 @@ export function genSetDynamicEvents(
 function genArrayExpression(elements: string[]) {
   return `[${elements.map(it => JSON.stringify(it)).join(', ')}]`
 }
+
+export function genEventHandler(
+  context: CodegenContext,
+  value: SimpleExpressionNode | undefined,
+) {
+  if (value && value.content.trim()) {
+    const isMemberExp = isMemberExpression(value.content, context.options)
+    const isInlineStatement = !(isMemberExp || fnExpRE.test(value.content))
+
+    if (isInlineStatement) {
+      const expr = context.withId(() => genExpression(value, context), {
+        $event: null,
+      })
+      const hasMultipleStatements = value.content.includes(`;`)
+      return [
+        '() => $event => ',
+        hasMultipleStatements ? '{' : '(',
+        ...expr,
+        hasMultipleStatements ? '}' : ')',
+      ]
+    } else {
+      return ['() => ', ...genExpression(value, context)]
+    }
+  }
+
+  return ['() => {}']
+}

+ 4 - 3
packages/compiler-vapor/src/generators/prop.ts

@@ -12,6 +12,7 @@ import type {
 } from '../ir'
 import { genExpression } from './expression'
 import { type CodeFragment, NEWLINE, genCall, genMulti } from './utils'
+import { toHandlerKey } from '@vue/shared'
 
 // only the static key prop will reach here
 export function genSetProp(
@@ -86,7 +87,7 @@ function genLiteralObjectProps(
 }
 
 export function genPropKey(
-  { key: node, modifier, runtimeCamelize, runtimeHandler }: IRProp,
+  { key: node, modifier, runtimeCamelize, handler }: IRProp,
   context: CodegenContext,
 ): CodeFragment[] {
   const { helper } = context
@@ -94,7 +95,7 @@ export function genPropKey(
   // static arg was transformed by v-bind transformer
   if (node.isStatic) {
     // only quote keys if necessary
-    const keyName = node.content
+    const keyName = handler ? toHandlerKey(node.content) : node.content
     return [
       [
         isSimpleIdentifier(keyName) ? keyName : JSON.stringify(keyName),
@@ -108,7 +109,7 @@ export function genPropKey(
   if (runtimeCamelize) {
     key = genCall(helper('camelize'), key)
   }
-  if (runtimeHandler) {
+  if (handler) {
     key = genCall(helper('toHandlerKey'), key)
   }
   return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']']

+ 1 - 1
packages/compiler-vapor/src/transform.ts

@@ -43,7 +43,7 @@ export interface DirectiveTransformResult {
   value: SimpleExpressionNode
   modifier?: '.' | '^'
   runtimeCamelize?: boolean
-  runtimeHandler?: boolean
+  handler?: boolean
 }
 
 // A structural directive transform is technically also a NodeTransform;

+ 2 - 5
packages/compiler-vapor/src/transforms/vOn.ts

@@ -6,7 +6,7 @@ import {
 import type { DirectiveTransform } from '../transform'
 import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir'
 import { resolveModifiers } from '@vue/compiler-dom'
-import { extend, makeMap, toHandlerKey } from '@vue/shared'
+import { extend, makeMap } from '@vue/shared'
 import { resolveExpression } from '../utils'
 import { EMPTY_EXPRESSION } from './utils'
 
@@ -61,14 +61,11 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => {
   }
 
   if (isComponent) {
-    if (arg.isStatic) {
-      arg = extend({}, arg, { content: toHandlerKey(arg.content) })
-    }
     const handler = exp || EMPTY_EXPRESSION
     return {
       key: arg,
       value: handler,
-      runtimeHandler: !arg.isStatic,
+      handler: true,
     }
   }