DEV Community

Rex Pan
Rex Pan

Posted on

Vue event Emit vs Callback Props

Emit events and Callback props will be compiled to the same code

HTMLElement

PlayGround

<button :onClick="increase">prop</button>
<button @click="increase">event</button>
Enter fullscreen mode Exit fullscreen mode
_createElementVNode("button", { onClick: increase }, "prop"),
_createElementVNode("button", { onClick: increase }, "event")
Enter fullscreen mode Exit fullscreen mode

Vue Component

PlayGround

<Comp :onClick="increase">prop +1</Comp>
<Comp @click="increase">emit +10</Comp>
Enter fullscreen mode Exit fullscreen mode
_createVNode(Comp, { onClick: increase }, { default: _withCtx(() => [ _createTextVNode("prop +1") ]), _: 1 /* STABLE */ }),
_createVNode(Comp, { onClick: increase }, { default: _withCtx(() => [ _createTextVNode("emit +10") ]), _: 1 /* STABLE */ })
Enter fullscreen mode Exit fullscreen mode

Combined

<button :onClick="increase" @click="increase">prop</button>
Enter fullscreen mode Exit fullscreen mode
_createElementVNode("button", { onClick: [increase, increase] }, "event")
Enter fullscreen mode Exit fullscreen mode

Drawback of Callback Props

No $event

We cannot use $event in :onClick.

<button :onClick="increase($event.x)">prop</button> <!-- FAIL -->
Enter fullscreen mode Exit fullscreen mode

Workaround is create anonymous callback but anonymous callback prop will not be cached and hurt performance. But this is easy to avoid.

PlayGround

<button :onClick="$event => increase($event.x)">prop</button> <!-- Workaround -->
<button @Click="increase($event.x)">prop</button>
Enter fullscreen mode Exit fullscreen mode
_createElementVNode("button", {
    onClick: $event => increase($event.x)
}, "prop", 8 /* PROPS */, _hoisted_1),
_createElementVNode("button", {
    onClick: _cache[0] || (_cache[0] = ($event) => (increase($event.x)))
}, "event")
Enter fullscreen mode Exit fullscreen mode

In v-for context will not be cached and anonymouse callback will have the same performance with emit.
Playground

<div v-for="x in xs" :key="x">
    <button :onClick="() => setY(x)">prop</button>
    <button @click="setY(x)">event</button>
</div>
Enter fullscreen mode Exit fullscreen mode
return (_openBlock(), _createElementBlock(_Fragment, null, _renderList(xs, (x) => {
    return _createElementVNode("div", { key: x }, [
        _createElementVNode("button", { onClick: () => setY(x) }, "prop", 8 /* PROPS */, _hoisted_1),
        _createElementVNode("button", { onClick: ($event) => (setY(x)) }, "event", 8 /* PROPS */, _hoisted_2),
    ])
}), 64 /* STABLE_FRAGMENT */))
Enter fullscreen mode Exit fullscreen mode

Emit definition will shadow props definition

Not anymore.

Pros of Callback Props

Event tracing

With following tree (PlayGround).

<App>
    <Comp2        :onCp1 @e2>
        <Comp1    :onCp1 @e1>
            <Comp :onCp  @e >
Enter fullscreen mode Exit fullscreen mode

On props callback stack, we can quick traced the App_logCp
is called by Comp2_onCp2 which is called by it's children Comp1_onCp1 which is then triggerd by Comp_onCp.

On emit, we only can traced App_logE 1 level down to Comp2_onE2.

In proper setup with source map like Vite, the <anonymous>:27:48 will be replace with App.vue.

Callback props stack
Error
    at Proxy.App_logCp (<anonymous>:27:48)
    at Proxy.Comp2_onCp2 (<anonymous>:34:34)
    at Proxy.Comp1_onCp1 (<anonymous>:34:34)
    at Comp_onCp (<anonymous>:28:32)
    at callWithErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1563:18)
    at callWithAsyncErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1571:17)
    at HTMLButtonElement.invoker (https://play.vuejs.org/vue.runtime.esm-browser.js:9281:5)

Emit stack
Error
    at App_logE (<anonymous>:28:46)
    at callWithErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1563:18)
    at callWithAsyncErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1571:17)
    at emit (https://play.vuejs.org/vue.runtime.esm-browser.js:2065:5)
    at https://play.vuejs.org/vue.runtime.esm-browser.js:8710:45
    at Comp2_onE2 (<anonymous>:35:27)
    at callWithErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1563:18)
    at callWithAsyncErrorHandling (https://play.vuejs.org/vue.runtime.esm-browser.js:1571:17)
    at emit (https://play.vuejs.org/vue.runtime.esm-browser.js:2065:5)
    at https://play.vuejs.org/vue.runtime.esm-browser.js:8710:45
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
petukhovart profile image
Artem Petukhov

and what style do you prefer to use in vue3 + ts ?