vue

Vue event Emit vs Callback Props

rexpan

Rex Pan

Posted on April 11, 2023

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
💖 💪 🙅 🚩
rexpan
Rex Pan

Posted on April 11, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related