feat: compound expression for `v-on` (#60)
This commit is contained in:
parent
fb4d9a1443
commit
26fee414ff
|
@ -12,6 +12,18 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > complex member expression w/ prefixIdentifiers: true 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > dynamic arg 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||
|
||||
|
@ -26,6 +38,34 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > dynamic arg with complex exp prefixing 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_on(n1, _ctx.event(_ctx.foo), (...args) => (_ctx.handler && _ctx.handler(...args)))
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > dynamic arg with prefixing 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_on(n1, _ctx.event, (...args) => (_ctx.handler && _ctx.handler(...args)))
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > event modifier 1`] = `
|
||||
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers, withKeys as _withKeys } from 'vue/vapor';
|
||||
|
||||
|
@ -59,6 +99,133 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", e => _ctx.foo(e))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > inline statement w/ prefixIdentifiers: true 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => (_ctx.foo($event)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > multiple inline statements w/ prefixIdentifiers: true 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => {_ctx.foo($event);_ctx.bar()})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should NOT add a prefix to $event if the expression is a function expression 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => {_ctx.i++;_ctx.foo($event)})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should NOT wrap as function if expression is already function expression (with Typescript) 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", (e: any): any => _ctx.foo(e))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should NOT wrap as function if expression is already function expression (with newlines) 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click",
|
||||
$event => {
|
||||
_ctx.foo($event)
|
||||
}
|
||||
)
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should NOT wrap as function if expression is already function expression 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => _ctx.foo($event))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should NOT wrap as function if expression is complex member expression 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should handle multi-line statement 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => {
|
||||
_ctx.foo();
|
||||
_ctx.bar()
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should handle multiple inline statement 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => {_ctx.foo();_ctx.bar()})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should not wrap keys guard if no key modifier is present 1`] = `
|
||||
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers } from 'vue/vapor';
|
||||
|
||||
|
@ -148,6 +315,32 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should wrap as function if expression is inline statement 1`] = `
|
||||
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_on(n1, "click", $event => (_ctx.i++))
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should wrap both for dynamic key event w/ left/right modifiers 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on, withKeys as _withKeys, withModifiers as _withModifiers } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_on(n1, _ctx.e, _withKeys(_withModifiers((...args) => (_ctx.test && _ctx.test(...args)), ["left"]), ["left"]))
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > should wrap keys guard for keyboard events or dynamic events 1`] = `
|
||||
"import { template as _template, children as _children, on as _on, withKeys as _withKeys, withModifiers as _withModifiers } from 'vue/vapor';
|
||||
|
||||
|
|
|
@ -124,30 +124,254 @@ describe('v-on', () => {
|
|||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test.todo('dynamic arg with prefixing')
|
||||
test.todo('dynamic arg with complex exp prefixing')
|
||||
test.todo('should wrap as function if expression is inline statement')
|
||||
test.todo('should handle multiple inline statement')
|
||||
test.todo('should handle multi-line statement')
|
||||
test.todo('inline statement w/ prefixIdentifiers: true')
|
||||
test.todo('multiple inline statements w/ prefixIdentifiers: true')
|
||||
test.todo(
|
||||
'should NOT wrap as function if expression is already function expression',
|
||||
)
|
||||
test.todo(
|
||||
test('dynamic arg with prefixing', () => {
|
||||
const { code } = compileWithVOn(`<div v-on:[event]="handler"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('dynamic arg with complex exp prefixing', () => {
|
||||
const { ir, code } = compileWithVOn(`<div v-on:[event(foo)]="handler"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(ir.vaporHelpers).contains('on')
|
||||
expect(ir.vaporHelpers).contains('renderEffect')
|
||||
expect(ir.helpers.size).toBe(0)
|
||||
expect(ir.operation).toEqual([])
|
||||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
element: 1,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'event(foo)',
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'handler',
|
||||
isStatic: false,
|
||||
},
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('should wrap as function if expression is inline statement', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click="i++"/>`)
|
||||
|
||||
expect(ir.vaporHelpers).contains('on')
|
||||
expect(ir.helpers.size).toBe(0)
|
||||
expect(ir.effect).toEqual([])
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
element: 1,
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'i++',
|
||||
isStatic: false,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('_on(n1, "click", $event => (_ctx.i++))')
|
||||
})
|
||||
|
||||
test('should handle multiple inline statement', () => {
|
||||
const { ir, code } = compileWithVOn(`<div @click="foo();bar()"/>`)
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: 'foo();bar()' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
// should wrap with `{` for multiple statements
|
||||
// in this case the return value is discarded and the behavior is
|
||||
// consistent with 2.x
|
||||
expect(code).contains('_on(n1, "click", $event => {_ctx.foo();_ctx.bar()})')
|
||||
})
|
||||
|
||||
test('should handle multi-line statement', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click="\nfoo();\nbar()\n"/>`)
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: '\nfoo();\nbar()\n' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
// should wrap with `{` for multiple statements
|
||||
// in this case the return value is discarded and the behavior is
|
||||
// consistent with 2.x
|
||||
expect(code).contains(
|
||||
'_on(n1, "click", $event => {\n_ctx.foo();\n_ctx.bar()\n})',
|
||||
)
|
||||
})
|
||||
|
||||
test('inline statement w/ prefixIdentifiers: true', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click="foo($event)"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: 'foo($event)' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
// should NOT prefix $event
|
||||
expect(code).contains('_on(n1, "click", $event => (_ctx.foo($event)))')
|
||||
})
|
||||
|
||||
test('multiple inline statements w/ prefixIdentifiers: true', () => {
|
||||
const { ir, code } = compileWithVOn(`<div @click="foo($event);bar()"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: 'foo($event);bar()' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
// should NOT prefix $event
|
||||
expect(code).contains(
|
||||
'_on(n1, "click", $event => {_ctx.foo($event);_ctx.bar()})',
|
||||
)
|
||||
})
|
||||
|
||||
test('should NOT wrap as function if expression is already function expression', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click="$event => foo($event)"/>`)
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: '$event => foo($event)' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('_on(n1, "click", $event => _ctx.foo($event))')
|
||||
})
|
||||
|
||||
test.fails(
|
||||
'should NOT wrap as function if expression is already function expression (with Typescript)',
|
||||
() => {
|
||||
const { ir, code } = compileWithVOn(
|
||||
`<div @click="(e: any): any => foo(e)"/>`,
|
||||
{ expressionPlugins: ['typescript'] },
|
||||
)
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: '(e: any): any => foo(e)' },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('_on(n1, "click", e => _ctx.foo(e))')
|
||||
},
|
||||
)
|
||||
test.todo(
|
||||
'should NOT wrap as function if expression is already function expression (with newlines)',
|
||||
)
|
||||
test.todo(
|
||||
'should NOT wrap as function if expression is already function expression (with newlines + function keyword)',
|
||||
)
|
||||
test.todo(
|
||||
'should NOT wrap as function if expression is complex member expression',
|
||||
)
|
||||
test.todo('complex member expression w/ prefixIdentifiers: true')
|
||||
test.todo('function expression w/ prefixIdentifiers: true')
|
||||
|
||||
test('should NOT wrap as function if expression is already function expression (with newlines)', () => {
|
||||
const { ir, code } = compileWithVOn(
|
||||
`<div @click="
|
||||
$event => {
|
||||
foo($event)
|
||||
}
|
||||
"/>`,
|
||||
)
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: {
|
||||
content: `
|
||||
$event => {
|
||||
foo($event)
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('should NOT add a prefix to $event if the expression is a function expression', () => {
|
||||
const { ir, code } = compileWithVOn(
|
||||
`<div @click="$event => {i++;foo($event)}"></div>`,
|
||||
{
|
||||
prefixIdentifiers: true,
|
||||
},
|
||||
)
|
||||
|
||||
expect(ir.operation[0]).toMatchObject({
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: '$event => {i++;foo($event)}' },
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('should NOT wrap as function if expression is complex member expression', () => {
|
||||
const { ir, code } = compileWithVOn(`<div @click="a['b' + c]"/>`)
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: `a['b' + c]` },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('complex member expression w/ prefixIdentifiers: true', () => {
|
||||
const { ir, code } = compileWithVOn(`<div @click="a['b' + c]"/>`)
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: `a['b' + c]` },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains(
|
||||
`_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))`,
|
||||
)
|
||||
})
|
||||
|
||||
test('function expression w/ prefixIdentifiers: true', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click="e => foo(e)"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
value: { content: `e => foo(e)` },
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('_on(n1, "click", e => _ctx.foo(e))')
|
||||
})
|
||||
|
||||
test('should error if no expression AND no modifier', () => {
|
||||
const onError = vi.fn()
|
||||
|
@ -366,14 +590,40 @@ describe('v-on', () => {
|
|||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
modifiers: { keys: ['left'] },
|
||||
modifiers: {
|
||||
keys: ['left'],
|
||||
nonKeys: [],
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test.todo('should wrap both for dynamic key event w/ left/right modifiers')
|
||||
test('should wrap both for dynamic key event w/ left/right modifiers', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @[e].left="test"/>`, {
|
||||
prefixIdentifiers: true,
|
||||
})
|
||||
|
||||
expect(ir.effect[0].operations).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'e',
|
||||
isStatic: false,
|
||||
},
|
||||
modifiers: {
|
||||
keys: ['left'],
|
||||
nonKeys: ['left'],
|
||||
options: [],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
})
|
||||
|
||||
test('should transform click.right', () => {
|
||||
const { code, ir } = compileWithVOn(`<div @click.right="test"/>`)
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
advancePositionWithClone,
|
||||
advancePositionWithMutation,
|
||||
createSimpleExpression,
|
||||
isMemberExpression,
|
||||
isSimpleIdentifier,
|
||||
locStub,
|
||||
walkIdentifiers,
|
||||
|
@ -33,6 +34,10 @@ import { SourceMapGenerator } from 'source-map-js'
|
|||
import { camelize, isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
||||
import type { Identifier } from '@babel/types'
|
||||
|
||||
// TODO: share this with compiler-core
|
||||
const fnExpRE =
|
||||
/^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
|
||||
|
||||
// remove when stable
|
||||
// @ts-expect-error
|
||||
function checkNever(x: never): never {}
|
||||
|
@ -508,15 +513,7 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
|
|||
|
||||
;(keys.length ? pushWithKeys : pushNoop)(() =>
|
||||
(nonKeys.length ? pushWithModifiers : pushNoop)(() => {
|
||||
if (oper.value && oper.value.content.trim()) {
|
||||
push('(...args) => (')
|
||||
genExpression(oper.value, context)
|
||||
push(' && ')
|
||||
genExpression(oper.value, context)
|
||||
push('(...args))')
|
||||
} else {
|
||||
push('() => {}')
|
||||
}
|
||||
genEventHandler()
|
||||
}),
|
||||
)
|
||||
},
|
||||
|
@ -524,6 +521,37 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
|
|||
!!options.length &&
|
||||
(() => push(`{ ${options.map((v) => `${v}: true`).join(', ')} }`)),
|
||||
)
|
||||
|
||||
function genEventHandler() {
|
||||
const exp = oper.value
|
||||
if (exp && exp.content.trim()) {
|
||||
const isMemberExp = isMemberExpression(exp.content, {
|
||||
// TODO: expression plugins
|
||||
expressionPlugins: [],
|
||||
})
|
||||
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
|
||||
const hasMultipleStatements = exp.content.includes(`;`)
|
||||
|
||||
if (isInlineStatement) {
|
||||
push('$event => ')
|
||||
push(hasMultipleStatements ? '{' : '(')
|
||||
const knownIds = Object.create(null)
|
||||
knownIds['$event'] = 1
|
||||
genExpression(exp, context, knownIds)
|
||||
push(hasMultipleStatements ? '}' : ')')
|
||||
} else if (isMemberExp) {
|
||||
push('(...args) => (')
|
||||
genExpression(exp, context)
|
||||
push(' && ')
|
||||
genExpression(exp, context)
|
||||
push('(...args))')
|
||||
} else {
|
||||
genExpression(exp, context)
|
||||
}
|
||||
} else {
|
||||
push('() => {}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
|
||||
|
@ -588,7 +616,11 @@ function genArrayExpression(elements: string[]) {
|
|||
|
||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||
|
||||
function genExpression(node: IRExpression, context: CodegenContext): void {
|
||||
function genExpression(
|
||||
node: IRExpression,
|
||||
context: CodegenContext,
|
||||
knownIds: Record<string, number> = Object.create(null),
|
||||
): void {
|
||||
const { push } = context
|
||||
if (isString(node)) return push(node)
|
||||
|
||||
|
@ -616,10 +648,13 @@ function genExpression(node: IRExpression, context: CodegenContext): void {
|
|||
const ids: Identifier[] = []
|
||||
walkIdentifiers(
|
||||
ast!,
|
||||
(id) => {
|
||||
(id, parent, parentStack, isReference, isLocal) => {
|
||||
if (isLocal) return
|
||||
ids.push(id)
|
||||
},
|
||||
true,
|
||||
[],
|
||||
knownIds,
|
||||
)
|
||||
if (ids.length) {
|
||||
ids.sort((a, b) => a.start! - b.start!)
|
||||
|
|
|
@ -71,6 +71,7 @@ export {
|
|||
export { TrackOpTypes, TriggerOpTypes, ReactiveFlags } from './constants'
|
||||
export {
|
||||
baseWatch,
|
||||
getCurrentEffect,
|
||||
onEffectCleanup,
|
||||
traverse,
|
||||
BaseWatchErrorCodes,
|
||||
|
|
|
@ -29,6 +29,7 @@ export {
|
|||
// effect
|
||||
stop,
|
||||
ReactiveEffect,
|
||||
getCurrentEffect,
|
||||
onEffectCleanup,
|
||||
// effect scope
|
||||
effectScope,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { getCurrentEffect, onEffectCleanup } from '@vue/reactivity'
|
||||
|
||||
export function on(
|
||||
el: HTMLElement,
|
||||
event: string,
|
||||
|
@ -5,4 +7,7 @@ export function on(
|
|||
options?: AddEventListenerOptions,
|
||||
) {
|
||||
el.addEventListener(event, handler, options)
|
||||
if (getCurrentEffect()) {
|
||||
onEffectCleanup(() => el.removeEventListener(event, handler, options))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue/vapor'
|
||||
const handleClick = () => {
|
||||
console.log('Hello, Vapor!')
|
||||
}
|
||||
|
||||
const i = ref(0)
|
||||
const event = ref('click')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -12,4 +16,12 @@ const handleClick = () => {
|
|||
<form>
|
||||
<button @click.prevent="handleClick">no submit</button>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
{{ i }}
|
||||
<button @[event].prevent="i++">Add by {{ event }}</button>
|
||||
<button @click="event = event === 'click' ? 'contextmenu' : 'click'">
|
||||
Change Event
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue