[vue-compat] @click not working on components
Version
3.1.2
Reproduction link
https://github.com/liamdebeasi/vue3-repro
Steps to reproduce
- Clone repo and run
npm install
. - Run
npm run serve
to start up a dev server. - Click the "Click me!" text and observe that nothing is logged to your Dev Tools Console.
- Quit the dev server and comment out the
config.resolve.alias
line invue.config.js
to disable the compat mode. - Run the dev server again.
- Click the "Click me!" text and observe that this time the "Component was clicked!" text is logged to your Dev Tools Console.
What is expected?
I would expect that the click handler is fired regardless of whether or not I am using the compat build.
What is actually happening?
The click handler is only firing when not using the compat build.
In Vue 2 you need to use the .native
modifier with v-on
bindings when the component itself is not emitting click
. I believe the compat build is aimed to align with this behavior.
Thanks! I tried that, but the click handler still does not fire. I am doing:
<app-cmp v-on:click.native="onClick()">Click me!</app-cmp>
The syntax you are using for the functional component seems off. It should be:
import { defineComponent, h } from 'vue';
const defineContainer = (name) => {
const Container = ((props, { slots }) => {
return h(name, props, slots.default && slots.default());
});
Container.displayName = name;
return Container;
};
(https://v3.vuejs.org/guide/migration/functional-components.html#_2-x-syntax)
You have other warnings telling you the next steps for migration in the console, so I'm closing this.
Remember to use the forum or the Discord chat to ask questions!
Please correct me if I am wrong, but I thought my original syntax is also valid? Passing a function to defineComponent
is the same thing as passing a setup
function according to https://v3.vuejs.org/api/global-api.html#definecomponent. This particular syntax seems to be used in Vue 3 tests implying that it is valid as well: https://github.com/vuejs/vue-next/blob/349eb0f0ad78f9cb491278eb4c7f9fe0c2e78b79/packages/server-renderer/__tests__/render.spec.ts#L100-L103
Also even when using the syntax you proposed and after making the migrations suggested in the console, the issue persists.
.native doesn't work in the compat view, so there doesn't even seem to be a workaround for this. There's not steps shown in the console at all if not using .native - just nothing fires.
FYI this breaks all of the ionic/vue system with 3.1
@posva Any tips how to get click events to fire in components we don't control with 3.1 compat? I.e. via the ionic framework. Since the removal of .native there doesn't seem to be any way to make this work. There's no console migration notices besides to remove .native - just no click events fire. I think maybe this is a bug and 3.1 should still allow the use of .native handler when the combability hasn't disabled it?
.native
is listed as being fully supported in compat build.
@liamdebeasi Your app is missing is the second part of the webpack config described in the migration guide - setting the compiler to compat mode:
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
return {
...options,
compilerOptions: {
compatConfig: {
MODE: 2
}
}
}
})
After I added this to your repro, click worked.
@inspire22 .native
is fully supported by the migration build. If you found a scenario where it doesn't work as intended, please open a new issue with a reproduction so we can check it out.
Thanks! I added the webpack config and changed the click handler to v-on:click.native
and now the click events are working as expected. I think I got confused by the The migration build runs in Vue 2 mode by default
line in the docs and thought that the webpack part was not necessary unless you needed to customize some part of the compat build.
edit: It might be good to revise the wording in the docs for this. I understood The migration build runs in Vue 2 mode by default
as "when you turn the migration build on, it will run in Vue 2 mode without having to explicitly tell it to". But since you need to set compatConfig: { MODE: 2 }
you really do need to explicitly tell it to run in Vue 2 mode. This led me to think that the Webpack config was not required. Thanks again for the help!
in step 3 of the Installation instructions it explicitly states:
In the build setup, alias vue to @vue/compat and enable compat mode via Vue compiler options.
Should that be changed to be clearer? Or should the introduction further up which threw you off be changed? If so, do you have something in mind? The folks in the docs-next repo would be happy to get suggestions.
Yeah I think the intro section threw me off with the way it was worded. I'll think of a clearer way to word it and make an issue/PR on the docs-next repo. Thanks!
@liamdebeasi I've made the .native updates to my ionic-based repo and click is still not fired - could this be related to the changed defineComponent vs what vue/ionic does? https://github.com/inspire22/ionic-click-bug
I'd been confused in thinking that .native wasn't supported because the included .eslint.rc had "plugin:vue/vue3-essential" which refuse to compile while .native was included.
Also - in ionic with vue version 3.0 .native wasn't required, correct? I guess this is a tricky interaction between the compat mode for vue and ionic's support for vue 3.0
@inspire22 Your repro is also missing the compiler config here:
@inspire22 The compiler config is not the solution in terms of Ionic.
Ionic exports Vue 3 components. But in compat mode, the app would treat them like Vue 2 components by default. You can do the following:
import {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton} from "@ionic/vue";
import {defineComponent} from "vue";
IonButton.compatConfig = {
MODE: 3
}
Which makes the alert pop up for me - without needing to add .native
.
To make this less messy, you might have to come up with a clever wrapper or sth. Idea:
import {IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonButton} from "@ionic/vue";
import {defineComponent} from "vue";
// you would of course import this from a module in each of your components
function fixCompMode(components) {
Object.entries(components).map(([name, component]) => {
if (!component.compatConfig && name.startsWith('Ion')) {
component.compatConfig = { MODE: 3 }
}
})
}
export default defineComponent({
name: "Home",
components: fixCompMode({
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonButton,
}),
methods: {
//...
This will add a very small one-time runtime overhead, but should not be harmful considering Vue 3 itself is faster than Vue 2.