From 9412c2053174d868dc0ab23e767d7749dbc616a8 Mon Sep 17 00:00:00 2001 From: Jevon <1787176370@qq.com> Date: Thu, 14 Mar 2024 14:15:45 +0800 Subject: [PATCH] feat: support v-on="obj" (#149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲智子 Kevin Deng --- .../transformElement.spec.ts.snap | 11 ++++++++ .../transforms/transformElement.spec.ts | 28 +++++++++++++++++++ .../compiler-vapor/src/generators/event.ts | 17 ++++++++++- .../src/generators/operation.ts | 4 ++- packages/compiler-vapor/src/ir.ts | 8 ++++++ packages/compiler-vapor/src/transforms/vOn.ts | 16 +++++++++-- packages/runtime-vapor/src/dom/event.ts | 9 ++++++ packages/runtime-vapor/src/index.ts | 2 +- 8 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap index 90e6e5260..3f70edb3f 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/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("
") + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => _setDynamicEvents(n0, _ctx.obj)) + return n0 +}" +`; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 8b7e98b93..3d17feca5 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -344,4 +344,32 @@ describe('compiler: element transform', () => { }, ]) }) + + test('v-on="obj"', () => { + const { code, ir } = compileWithElementTransform(`
`) + 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)') + }) }) diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index 9c7514bf8..df6738de2 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/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(', ')}]` } diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index a6234e5db..e08022a7d 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/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: diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 82e47e6cc..6b6de7026 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/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 diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 748e89f9e..d758754ed 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/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 } diff --git a/packages/runtime-vapor/src/dom/event.ts b/packages/runtime-vapor/src/dom/event.ts index eb2ae6aa2..f93b5b160 100644 --- a/packages/runtime-vapor/src/dom/event.ts +++ b/packages/runtime-vapor/src/dom/event.ts @@ -122,3 +122,12 @@ const delegatedEventHandler = (e: Event) => { : node.parentNode } } + +export function setDynamicEvents( + el: HTMLElement, + events: Record any>, +) { + for (const [event, eventHandler] of Object.entries(events)) { + on(el, event, () => eventHandler, { effect: true }) + } +} diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 3a7384f23..b2cac49cb 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/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'