Skip to content

[Vue compat] @click not working on a component #4566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
SupertigerDev opened this issue Sep 12, 2021 · 22 comments
Open

[Vue compat] @click not working on a component #4566

SupertigerDev opened this issue Sep 12, 2021 · 22 comments

Comments

@SupertigerDev
Copy link

SupertigerDev commented Sep 12, 2021

Version

3.2.11

Reproduction link

codesandbox.io

Steps to reproduce

Clicking on "click mee"

What is expected?

Should log "click" in the console

What is actually happening?

Not doing anything


I tried adding COMPILER_V_ON_NATIVE: false to vue.config.js but it seems to make no difference. This issue only happens in the compat build of vue

@Justineo
Copy link
Member

You need to use the .native modifier.

@SupertigerDev
Copy link
Author

SupertigerDev commented Sep 12, 2021

Oh, but in the documents, it says to remove all .natives? confused
https://v3.vuejs.org/guide/migration/v-on-native-modifier-removed.html#_2-x-syntax
I also get this huge error in my real project to actually remove .native.
image

@SupertigerDev
Copy link
Author

SupertigerDev commented Sep 12, 2021

Also even with the .native modifier, it still doesnt work with the compat build of vue 3.

https://codesandbox.io/s/sharp-proskuriakova-woc9s?file=/src/App.vue

@Justineo
Copy link
Member

Are you using the migration build? The compiler flag you mentioned can only work with the migration build. See https://v3.vuejs.org/guide/migration/migration-build.html#migration-build

@SupertigerDev
Copy link
Author

Yes, as my title says, im using the vue 3 compat build which is the migration build.

@SupertigerDev
Copy link
Author

SupertigerDev commented Sep 12, 2021

Could you reopen the issue please as this seems like a bug. I've been stuck with this issue all day :(

@LinusBorg LinusBorg reopened this Sep 12, 2021
@LinusBorg
Copy link
Member

LinusBorg commented Sep 13, 2021

So I've been looking into this, and it doesn't really qualify as a bug. Rather, it's a complicated interaction between multiple breaking changes and their corresponding COMPAT flags/behaviors. This might need a special entry in the migration docs.

Short summary

  • in Vue 2, the parent is responsible for applying native root node listeners, in Vue 3, it's the child.
  • When COMPILER_V_ON_NATIVE is enabled and the parent used .native, we do emulate this by passing a flag along the event to the child component which can then properly handle the "native" event.
  • When COMPILER_V_ON_NATIVE is disabled and the parent doesn't use .native anymore, we pass the event like a normal component event listener to the child (no additional flag).
  • The child, running in Vue 2 compat mode, does treat all incoming listeners as component event listeners and doesn't add anything to the root node.

Solution

In the child component, the following compatConfig has to be set:

export default {
  compatConfig: {
    INSTANCE_LISTENERS: false,
  }
}

This has the following effects:

  1. The component instance no longer provides this.$listeners (Vue 2 API, not present in Vue 3)
  2. All incoming event listeners that are not declared in the component's emits: [] option are added as native listeners to the template root element. (new Vue 3 behavior)

If the child is already fully migrated and can run in Vue 3 mode, the problem would also be solved:

export default {
  compatConfig: {
    MODE: 3,
  }
}

Concerning Migration documentation

So this is one of the most complex migration steps in fact, as it involves a change in behavior in the parent and child interaction.

  • In Vue 2, the parent was responsible to take care of native listeners marked with .native.
  • In Vue 3, the child does take care of them by treating all non-declared events as native.
  • This touches on multiple changes:
    • .native removal
    • $listeners removal and non-declared events now being part of $attrs
    • Requirement to explicitly declare a component's emitted events with emits

So what would be a good migration strategy that we can document? I think this would be good

  1. Keep .native in templates until the children are ready to handle native events in the Vue 3 way
  2. First, migrate any children that use $listeners to use $attrs instead (usually used in combination with inheritAttrs: false)
  3. document emitted events in child components with emits: []
  4. In each of these child components, disable INSTANCE_LISTENERS compat behavior
  5. Now remove all usage of .native

/cc @vuejs/docs

@cyfung1031

This comment has been minimized.

@cyfung1031

This comment has been minimized.

@cyfung1031

This comment has been minimized.

@SupertigerDev
Copy link
Author

I fixed my issue by adding INSTANCE_LISTENERS: false as suggested 😀

@cyfung1031

This comment has been minimized.

@LinusBorg

This comment has been minimized.

@SupertigerDev

This comment has been minimized.

@markhalliwell
Copy link

Thank you @LinusBorg! I have been trying to figure out why this wasn't working. The only thing I will add to the above solution is some more details around where to put INSTANCE_LISTENERS. I attempted to add this to compilerOptions in vue.config.js prior to finding this issue and it did not seem to work at all. It would seem this is a component specific compat behavior.

@LinusBorg
Copy link
Member

LinusBorg commented Jan 26, 2022

But it already says it has to be set "in the child component".

And on top, the migration guide explains that all Compat flags that need to be set in the compiler Options are prefixed with COMPILER_*

I'm not sure how to make it more clear, do you have a concrete suggestion?

@markhalliwell
Copy link

And on top, the migration guide explains that all Compat flags that need to be set in the compiler Options are prefixed with COMPILER_*

Yes, however, it is not explicit that only the flags that are prefixed with COMPILER_ will work in the build setup and not the other flags that are not prefixed with it. This is what is confusing.

I'm not sure how to make it more clear, do you have a concrete suggestion?

On the Compat Configuration section, it defines 4 different places you add these flags. It doesn't offer any real explanation as to the purpose behind which one should be used, why and when it would make sense to use one over the other (as in this issue's case).

Instead, the guide comes across as "provide these flags where ever you want". So, when I follow the installation in the guide for vue-cli (which already has compatConfig in the compilerOptions) and my immediate thought is: "Oh! I can just put any compat flags here."

Which is what I did and why I couldn't get it to work prior to finding this issue. That is what I meant by "adding more details around where to put INSTANCE_LISTENERS":

Adding a simple and general disclaimer on the guide that there might be times where you may need to add these flags specifically to components to get them to work properly.

@LinusBorg
Copy link
Member

Thanks, that's something to work with 👍

@hokkaido
Copy link

hokkaido commented Mar 3, 2023

@LinusBorg Stupid question, but if I have a project where I set compatConfig globally to Mode: 2 and then import a vanilla vue 3 component library (external npm dependency), does this force those components to run in Mode: 2 if they don't specifically declare a compatConfig with Mode: 3 themselves?

@LinusBorg
Copy link
Member

Yes. So to leave them in Vue 3 more you would have to add that config to those components. Can be done at runtime.

@hokkaido
Copy link

hokkaido commented Mar 3, 2023

@LinusBorg

Thanks. Like this? If so, I think this behaviour and workaround should be documented, will try to open a PR with the docs project.

<script lang="ts">
import { ExternalVue3Component } from "@acme-corp/ui-toolkit";
(ExternalVue3Component as any).compatConfig = { MODE: 3 };
//...
</script>

Background to my question: BootstrapVue, which is a fairly prevalent and widely used Vue library, requires setting MODE: 2 globally with their compat build - they don't have a native Vue 3.0 package yet. This in turn leads to downstream issues if you include external libraries written in Vue 3.0, which is kinda what you do when upgrading a project from Vue 2.0 to Vue 3.0.

I would have assumed that Vue somehow would prevent the described behavior when encountering external components written in Vue 3.0 - what does it even mean to force a component written with the composition API to conform to MODE: 2?

@nicooprat
Copy link

Thanks for the clear explanations Linus.

We had the issue while migrating to ElementPlus through the compat build, and we fixed it like this:

// main.ts
import { ElForm } from 'element-plus';

ElForm.compatConfig = {
  ...ElForm.compatConfig,
  INSTANCE_LISTENERS: false,
};

Then we can still prevent unwanted form submissions this way:

<el-form @submit.prevent.stop="handleSubmit" />

sfreytag added a commit to sfreytag/v3-migration-guide that referenced this issue Jul 26, 2023
As described in this GitHub issue comments:

vuejs/core#4566 (comment)

and suggested as a useful addition to the docs.
ianef added a commit to XactSystems/b-pagination-table that referenced this issue Apr 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants