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

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
Jevon 2024-03-14 14:15:45 +08:00 committed by GitHub
parent 421eba3e01
commit 9412c20531
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 90 additions and 5 deletions

View File

@ -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
}"
`;

View File

@ -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)')
})
})

View File

@ -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(', ')}]`
}

View File

@ -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:

View File

@ -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

View File

@ -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
}

View File

@ -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 })
}
}

View File

@ -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'