Emit events and Callback props will be compiled to the same code
HTMLElement
<button :onClick="increase">prop</button>
<button @click="increase">event</button>
_createElementVNode("button", { onClick: increase }, "prop"),
_createElementVNode("button", { onClick: increase }, "event")
Vue Component
<Comp :onClick="increase">prop +1</Comp>
<Comp @click="increase">emit +10</Comp>
_createVNode(Comp, { onClick: increase }, { default: _withCtx(() => [ _createTextVNode("prop +1") ]), _: 1 /* STABLE */ }),
_createVNode(Comp, { onClick: increase }, { default: _withCtx(() => [ _createTextVNode("emit +10") ]), _: 1 /* STABLE */ })
Combined
<button :onClick="increase" @click="increase">prop</button>
_createElementVNode("button", { onClick: [increase, increase] }, "event")
Drawback of Callback Props
No $event
We cannot use $event
in :onClick
.
<button :onClick="increase($event.x)">prop</button> <!-- FAIL -->
Workaround is create anonymous callback but anonymous callback prop will not be cached and hurt performance. But this is easy to avoid.
<button :onClick="$event => increase($event.x)">prop</button> <!-- Workaround -->
<button @Click="increase($event.x)">prop</button>
_createElementVNode("button", {
onClick: $event => increase($event.x)
}, "prop", 8 /* PROPS */, _hoisted_1),
_createElementVNode("button", {
onClick: _cache[0] || (_cache[0] = ($event) => (increase($event.x)))
}, "event")
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>
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 */))
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 >
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
Top comments (1)
and what style do you prefer to use in vue3 + ts ?