【问题标题】:Vue v-on:click 对组件不起作用
【发布时间】:2017-05-19 10:19:48
【问题描述】:

我正在尝试在组件中使用 on click 指令,但它似乎不起作用。当我单击组件时,当我应该在控制台中获得“单击测试”时,什么都不会发生。我在控制台中没有看到任何错误,所以我不知道我做错了什么。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuetest</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

App.vue

<template>
  <div id="app">
    <test v-on:click="testFunction"></test>
  </div>
</template>

<script>
import Test from './components/Test'

export default {
  name: 'app',
  methods: {
    testFunction: function (event) {
      console.log('test clicked')
    }
  },
  components: {
    Test
  }
}
</script>

Test.vue(组件)

<template>
  <div>
    click here
  </div>
</template>

<script>
export default {
  name: 'test',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

【问题讨论】:

    标签: javascript vue-component vuejs2 vue.js


    【解决方案1】:

    如果你想在组件的根元素上监听原生事件,你必须使用.native修饰符v-on,如下所示:

    <template>
      <div id="app">
        <test v-on:click.native="testFunction"></test>
      </div>
    </template>
    

    或者简而言之,正如评论中所建议的那样,您也可以这样做:

    <template>
      <div id="app">
        <test @click.native="testFunction"></test>
      </div>
    </template>
    

    Reference to read more about native event

    【讨论】:

    • 或简写@click.native="testFunction"
    • 什么是原生事件或者它与其他正常事件有何不同?为什么这种根元素的特殊情况???
    • 不建议在 vue 中使用原生修饰符。只有在绝对必要时才使用它。请参阅@jim-mcneely 了解实现此目的的正确方法,即从子元素发出事件,然后在父元素中恢复它。
    • 这是native events的正确链接
    【解决方案2】:

    我认为$emit 功能更适合我认为您所要求的。它使您的组件与 Vue 实例分离,以便在许多情况下可重用。

    // Child component
    <template>
      <div id="app">
        <test @click="$emit('test-click')"></test>
      </div>
    </template>
    

    在 HTML 中使用它

    // Parent component
    <test @test-click="testFunction">
    

    【讨论】:

    • 我相信这是正确的答案。在组件内处理事件的链接。不关心要调用的点击事件的“版本”的父组件。我实际上在组件@click="$emit('click')" 中将其实现为下面的答案,这样父组件只需使用常规的@click
    • 我有点困惑。 $emit 部分是否应该在测试组件模板中?
    • @NelsonRodriguez 说了什么。使用@click="$emit('click')"
    • .native 修饰符相比,这绝对是更明确的解决方案,并且使父组件更少依赖子组件的实现细节,这几乎总是一个加分项。
    【解决方案3】:

    这是@Neps' answer,但有详细信息。


    注意@Saurabh's answer 更适合您不想修改组件或无权访问它。


    为什么@click 不能正常工作?

    组件很复杂。一个组件可以是一个小的花哨的按钮包装器,另一个可以是一个包含一堆逻辑的整个表格。当绑定v-model 或使用v-on 时,Vue 不知道你究竟期望什么,所以所有这些都应该由组件的创建者处理。

    如何处理点击事件

    根据Vue docs$emit 将事件传递给父级。来自文档的示例:

    主文件

    <blog-post
      @enlarge-text="onEnlargeText"
    />
    

    组件

    <button @click="$emit('enlarge-text')">
      Enlarge text
    </button>
    

    (@v-onshorthand)

    组件处理原生 click 事件并发出父级的 @enlarge-text="..."

    enlarge-text 可以替换为click,使其看起来像是在处理原生点击事件:

    <blog-post
      @click="onEnlargeText"
    ></blog-post>
    
    <button @click="$emit('click')">
      Enlarge text
    </button>
    

    但这还不是全部。 $emit 允许通过事件传递特定值。在原生click的情况下,值为MouseEvent(与Vue无关的JS事件)。

    Vue 将该事件存储在 $event 变量中。因此,最好使用事件发出 $event,以营造原生事件使用的印象:

    <button v-on:click="$emit('click', $event)">
      Enlarge text
    </button>
    

    【讨论】:

      【解决方案4】:

      正如 Chris Fritz (Vue.js Core Team Emeriti) 在 VueCONF US 2019 中提到的那样

      如果我们让 Kia 输入 .native,然后基本输入的根元素突然从输入变为标签,则此组件已损坏且不明显,事实上,您甚至可能无法立即捕捉到它,除非您有一个很好的测试。相反,通过避免使用 .native 修饰符 我目前认为这是一种反模式,并将在 Vue 3 中删除,您将能够明确定义父级可能关心哪个元素监听器被添加到...

      使用 Vue 2

      使用$listeners:

      因此,如果您使用的是 Vue 2,解决此问题的更好选择是使用 完全透明的包装器 逻辑。为此,Vue 提供了一个 $listeners 属性,其中包含在组件上使用的侦听器对象。例如:

      {
        focus: function (event) { /* ... */ }
        input: function (value) { /* ... */ },
      }
      

      然后我们只需要将v-on="$listeners" 添加到test 组件中,例如:

      Test.vue(子组件)

      <template>
        <div v-on="$listeners">
          click here
        </div>
      </template>
      

      现在&lt;test&gt; 组件是一个完全透明的包装器,这意味着它可以像普通的&lt;div&gt; 元素一样使用:所有侦听器都可以工作,而无需.native 修饰符。

      演示:

      Vue.component('test', {
        template: `
          <div class="child" v-on="$listeners">
            Click here
          </div>`
      })
      
      new Vue({
        el: "#myApp",
        data: {},
        methods: {
          testFunction: function(event) {
            console.log('test clicked')
          }
        }
      })
      div.child{border:5px dotted orange; padding:20px;}
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
      <div id="myApp">
        <test @click="testFunction"></test>
      </div>

      使用$emit方法:

      我们也可以使用$emit方法来达到这个目的,它可以帮助我们在父组件中监听子组件的事件。为此,我们首先需要从子组件发出custom event,例如:

      Test.vue(子组件)

      <test @click="$emit('my-event')"></test>
      

      重要提示:始终使用 kebab-case 作为事件名称。有关更多信息和有关这一点的演示,请查看此答案:VueJS passing computed value from component to parent

      现在,我们只需要在父组件中监听这个发出的自定义事件,比如:

      App.vue

      <test @my-event="testFunction"></test>
      

      所以基本上,我们将简单地使用v-on:my-event@my-event,而不是v-on:click 或简写@click

      演示:

      Vue.component('test', {
        template: `
          <div class="child" @click="$emit('my-event')">
            Click here
          </div>`
      })
      
      new Vue({
        el: "#myApp",
        data: {},
        methods: {
          testFunction: function(event) {
            console.log('test clicked')
          }
        }
      })
      div.child{border:5px dotted orange; padding:20px;}
      <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
      <div id="myApp">
        <test @my-event="testFunction"></test>
      </div>

      使用 Vue 3

      使用v-bind="$attrs":

      Vue 3 将在许多方面让我们的生活变得更加轻松。一个例子是,它可以帮助我们创建一个更简单的透明包装器,只需使用v-bind="$attrs",只需使用更少的配置即可。通过在子组件上使用它,我们的监听器不仅可以直接从父组件工作,而且任何其他属性也可以像普通的&lt;div&gt; 一样工作。

      所以,关于这个问题,我们不需要在 Vue 3 中更新任何内容,您的代码仍然可以正常工作,因为 &lt;div&gt; 是这里的根元素,它会自动监听所有子事件。

      演示 #1:

      const { createApp } = Vue;
      
      const Test = {
        template: `
          <div class="child">
            Click here
          </div>`
      };
      
      const App = {
        components: { Test },
        setup() {
          const testFunction = event => {
            console.log("test clicked");
          };
          return { testFunction };
        }
      };
      
      createApp(App).mount("#myApp");
      div.child{border:5px dotted orange; padding:20px;}
      <script src="//unpkg.com/vue@next"></script>
      <div id="myApp">
        <test v-on:click="testFunction"></test>
      </div>

      但是,对于具有嵌套元素的复杂组件,我们需要将属性和事件应用到 &lt;input /&gt; 而不是父标签,我们可以简单地使用 v-bind="$attrs"

      演示 #2:

      const { createApp } = Vue;
      
      const BaseInput = {
        props: ['label', 'value'],
        template: `
          <label>
            {{ label }}
            <input v-bind="$attrs">
          </label>`
      };
      
      const App = {
        components: { BaseInput },
        setup() {
          const search = event => {
            console.clear();
            console.log("Searching...", event.target.value);
          };
          return { search };
        }
      };
      
      createApp(App).mount("#myApp");
      input{padding:8px;}
      <script src="//unpkg.com/vue@next"></script>
      <div id="myApp">
        <base-input 
          label="Search: "
          placeholder="Search"
          @keyup="search">
        </base-input><br/>
      </div>

      【讨论】:

        【解决方案5】:

        有点冗长,但我就是这样做的:

        @click="$emit('click', $event)"

        更新:@sparkyspider 添加的示例

        <div-container @click="doSomething"></div-container>
        

        div-container 组件中...

        <template>
          <div @click="$emit('click', $event);">The inner div</div>
        </template>
        

        【讨论】:

        • 这个去哪儿了?你为什么把它放在那里?请为查看此答案的人添加更多详细信息?
        • 在这个例子中,这将被放置在 Test.vue 组件中的 div 标签上。然后在 App.vue 中使用组件测试时可以使用 v-on:click="testFunction" 或 @click="testFunction"
        • 我将其更改为$emit,但没有任何反应。除了$emit,我还需要做什么吗? jsfiddle.net/xwvhy6a3
        • @RichardBarraclough 您的组件现在会发出您的自定义事件“clickTreeItem”。接下来是在使用该组件时处理该事件的处理方式: v-on:myEvent="myMethod"
        【解决方案6】:

        组件的原生事件不能直接从父元素访问。相反,您应该尝试v-on:click.native="testFunction",或者您也可以从Test 组件发出一个事件。喜欢v-on:click="$emit('click')"

        【讨论】:

          【解决方案7】:

          使用@click.native 的一个用例是当您创建一个自定义组件并且您想要监听自定义组件上的点击事件时。例如:

          #CustomComponent.vue
          <div>
            <span>This is a custom component</span>
          </div>
          
          #App.vue
          <custom-component @click.native="onClick"></custom-component>
          

          @click.native 总是适用于这种情况。

          【讨论】:

            【解决方案8】:

            来自documentation

            由于 JavaScript 的限制,Vue 无法检测到数组的以下更改:

            1. 当您直接使用索引设置项目时,例如vm.items[indexOfItem] = newValue
            2. 当您修改数组的长度时,例如vm.items.length = newLength

            在我从 Angular 迁移到 VUE 时,我偶然发现了这个问题。修复很容易,但很难找到:

            setValue(index) {
                Vue.set(this.arr, index, !this.arr[index]);
                this.$forceUpdate(); // Needed to force view rerendering
            }
            

            【讨论】:

              猜你喜欢
              • 2016-08-15
              • 2020-04-07
              • 2023-03-10
              • 2018-12-14
              • 2021-10-05
              • 2017-07-05
              • 1970-01-01
              • 2019-09-04
              • 1970-01-01
              相关资源
              最近更新 更多