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

feat: support v-on="obj" (#149)

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

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

@@ -75,3 +75,14 @@ export function render(_ctx) {
   return n0
 }"
 `;
+
+exports[`compiler: element transform > v-on="obj" 1`] = `
+"import { renderEffect as _renderEffect, setDynamicEvents as _setDynamicEvents, template as _template } from 'vue/vapor';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = t0()
+  _renderEffect(() => _setDynamicEvents(n0, _ctx.obj))
+  return n0
+}"
+`;

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

@@ -344,4 +344,32 @@ describe('compiler: element transform', () => {
       },
     ])
   })
+
+  test('v-on="obj"', () => {
+    const { code, ir } = compileWithElementTransform(`<div v-on="obj" />`)
+    expect(code).toMatchSnapshot()
+    expect(ir.block.effect).toMatchObject([
+      {
+        expressions: [
+          {
+            type: NodeTypes.SIMPLE_EXPRESSION,
+            content: 'obj',
+            isStatic: false,
+          },
+        ],
+        operations: [
+          {
+            type: IRNodeTypes.SET_DYNAMIC_EVENTS,
+            element: 0,
+            event: {
+              type: NodeTypes.SIMPLE_EXPRESSION,
+              content: 'obj',
+              isStatic: false,
+            },
+          },
+        ],
+      },
+    ])
+    expect(code).contains('_setDynamicEvents(n0, _ctx.obj)')
+  })
 })

+ 16 - 1
packages/compiler-vapor/src/generators/event.ts

@@ -1,6 +1,6 @@
 import { fnExpRE, isMemberExpression } from '@vue/compiler-dom'
 import type { CodegenContext } from '../generate'
-import type { SetEventIRNode } from '../ir'
+import type { SetDynamicEventsIRNode, SetEventIRNode } from '../ir'
 import { genExpression } from './expression'
 import {
   type CodeFragment,
@@ -93,6 +93,21 @@ export function genSetEvent(
   }
 }
 
+export function genSetDynamicEvents(
+  oper: SetDynamicEventsIRNode,
+  context: CodegenContext,
+): CodeFragment[] {
+  const { vaporHelper } = context
+  return [
+    NEWLINE,
+    ...genCall(
+      vaporHelper('setDynamicEvents'),
+      `n${oper.element}`,
+      genExpression(oper.event, context),
+    ),
+  ]
+}
+
 function genArrayExpression(elements: string[]) {
   return `[${elements.map(it => JSON.stringify(it)).join(', ')}]`
 }

+ 3 - 1
packages/compiler-vapor/src/generators/operation.ts

@@ -1,7 +1,7 @@
 import { type IREffect, IRNodeTypes, type OperationNode } from '../ir'
 import type { CodegenContext } from '../generate'
 import { genInsertNode, genPrependNode } from './dom'
-import { genSetEvent } from './event'
+import { genSetDynamicEvents, genSetEvent } from './event'
 import { genFor } from './for'
 import { genSetHtml } from './html'
 import { genIf } from './if'
@@ -38,6 +38,8 @@ export function genOperation(
       return genSetText(oper, context)
     case IRNodeTypes.SET_EVENT:
       return genSetEvent(oper, context)
+    case IRNodeTypes.SET_DYNAMIC_EVENTS:
+      return genSetDynamicEvents(oper, context)
     case IRNodeTypes.SET_HTML:
       return genSetHtml(oper, context)
     case IRNodeTypes.SET_REF:

+ 8 - 0
packages/compiler-vapor/src/ir.ts

@@ -21,6 +21,7 @@ export enum IRNodeTypes {
   SET_DYNAMIC_PROPS,
   SET_TEXT,
   SET_EVENT,
+  SET_DYNAMIC_EVENTS,
   SET_HTML,
   SET_REF,
   SET_MODEL_VALUE,
@@ -94,6 +95,12 @@ export interface SetDynamicPropsIRNode extends BaseIRNode {
   props: IRProps[]
 }
 
+export interface SetDynamicEventsIRNode extends BaseIRNode {
+  type: IRNodeTypes.SET_DYNAMIC_EVENTS
+  element: number
+  event: SimpleExpressionNode
+}
+
 export interface SetTextIRNode extends BaseIRNode {
   type: IRNodeTypes.SET_TEXT
   element: number
@@ -172,6 +179,7 @@ export type OperationNode =
   | SetDynamicPropsIRNode
   | SetTextIRNode
   | SetEventIRNode
+  | SetDynamicEventsIRNode
   | SetHtmlIRNode
   | SetRefIRNode
   | SetModelValueIRNode

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

@@ -14,14 +14,26 @@ const delegatedEvents = /*#__PURE__*/ makeMap(
 
 export const transformVOn: DirectiveTransform = (dir, node, context) => {
   let { arg, exp, loc, modifiers } = dir
-  if (!exp && !modifiers.length) {
+  if (!exp && (!modifiers.length || !arg)) {
     context.options.onError(
       createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc),
     )
   }
 
   if (!arg) {
-    // TODO support v-on="{}"
+    // v-on="obj"
+    if (exp) {
+      context.registerEffect(
+        [exp],
+        [
+          {
+            type: IRNodeTypes.SET_DYNAMIC_EVENTS,
+            element: context.reference(),
+            event: exp,
+          },
+        ],
+      )
+    }
     return
   }
 

+ 9 - 0
packages/runtime-vapor/src/dom/event.ts

@@ -122,3 +122,12 @@ const delegatedEventHandler = (e: Event) => {
         : node.parentNode
   }
 }
+
+export function setDynamicEvents(
+  el: HTMLElement,
+  events: Record<string, (...args: any[]) => any>,
+) {
+  for (const [event, eventHandler] of Object.entries(events)) {
+    on(el, event, () => eventHandler, { effect: true })
+  }
+}

+ 1 - 1
packages/runtime-vapor/src/index.ts

@@ -84,7 +84,7 @@ export {
   setDynamicProp,
   setDynamicProps,
 } from './dom/prop'
-export { on, delegate, delegateEvents } from './dom/event'
+export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
 export { setRef } from './dom/templateRef'
 
 export { defineComponent } from './apiDefineComponent'