Vue组件$发射两次

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue组件$发射两次相关的知识,希望对你有一定的参考价值。

我最近试图在一些真实世界的应用程序中重用我的Vue组件,以删除不必要的重复和<divs>混乱。

但我这样做有困难。几个小时后,我设法“完成”它,但现在事件发生了两次,我不确切地知道为什么。

我做了一个基本设置来显示问题:

Vue.component("bs-select",{
template:
 `<div class="form-group">
    <label class="control-label">{{ label }}</label>
    <select2
      ref="select2control"
      :options="options"
      :value="value"
      @input="chosenOption"
    ></select2>
  </div>`,
  props: ["value", "label", "options"],
  methods: {
    chosenOption(val) {
      this.$emit("input", val);
    }
  }
});

Vue.component("select2",{
template:
 `<select :value="value" style="width: 100%">
    <option value="" selected disabled>Choose...</option>
    <option v-if="!options">{{ value }}</option>
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select>`,
  props: ["value", "options"],
  mounted: function() {
    const vm = this;

    $(vm.$el)
      .select2()
      .on("change", function() {
        console.log("CHANGE", vm.$el.value);
        vm.$emit("input", vm.$el.value);
      });
  },
  watch: {
    value: function(val) {
      $(this.$el)
        .val(val)
        .trigger("change");
    }
  }
});

new Vue({
  el: "#app",
  data: {
    test: "bug",
    options: [
    {
      value: "hello",
      text: "Hello"
    },
    {
      value: "bug",
      text: "Bug"
    }
    ]
  }
})
* {
  font-family: Arial;
  font-size: 10px;
}

div {
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

<div id="app">
  <bs-select v-model="test" :options="options"></bs-select>
  <br><br>
  <button @click="test = 'bug'">
  Set 'test' variable to 'bug' (Two-way check)
  </button>
{{ test }}
</div>

<div>
Event is firing twice in console...
</div>

我也google了很多,并且没有得出结论为什么会发生这种情况和/或如何解决这个问题。

任何帮助是极大的赞赏。

答案

我不确定你到底想要做什么,但我的猜测是你两次开始onchange事件,一次实际改变,一次在观察者中。无论如何,当有像这样的vue解决方案时,你真的不需要像这样使用听众:

 <div id="app">
  <bs-select :value="test" v-on:change-value="test = $event" :options="options"></bs-select>
  <br><br>
  {{ test }}

  <br><br>

  <button @click="test = 'bug'">
  Set 'test' variable to 'bug'
  </button>
</div>

<div>
Event is firing twice in console...
</div>

零件:

Vue.component("bs-select",{
template:
 `<select :value="value" v-on:change="changeVal" style="width: 100%">
    <option value="" selected disabled>Choose...</option>
    <option v-if="!options">{{ value }}</option>
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select>`,
  props: ["value", "options"],
  methods: {
    changeVal: function(event) {
        this.$emit('change-value', event.target.value)
    }
  }
});

new Vue({
  el: "#app",
  data: {
    test: "bug",
    options: [
    {
      value: "hello",
      text: "Hello"
    },
    {
      value: "bug",
      text: "Bug"
    }
    ]
  }
})

https://jsfiddle.net/kv3bq1dw/

另一答案

在询问了我的一些朋友之后,人们发现“改变”触发器必须在beforeUpdate中。

所以,解决的代码如下所示:

Vue.component("bs-select",{
template:
 `<div class="form-group">
    <label class="control-label">{{ label }}</label>
    <select2
      ref="select2control"
      :options="options"
      :value="value"
      @input="chosenOption"
    ></select2>
  </div>`,
  props: ["value", "label", "options"],
  methods: {
    chosenOption(val) {
      this.$emit("input", val);
    }
  }
});

Vue.component("select2",{
template:
 `<select :value="value" style="width: 100%">
    <option value="" selected disabled>Choose...</option>
    <option v-if="!options">{{ value }}</option>
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select>`,
  props: ["value", "options"],
  mounted: function() {
    const vm = this;

    $(vm.$el)
      .select2()
      .on("change", function() {
        console.log("CHANGE", vm.$el.value);
        vm.$emit("input", vm.$el.value);
      });
  },
	beforeUpdate() {
  	$(this.$el).val(this.value).trigger('change.select2')
  }
});

new Vue({
  el: "#app",
  data: {
    test: "hello",
    options: [
    {
      value: "hello",
      text: "Hello"
    },
    {
      value: "solved",
      text: "Solved"
    }
    ]
  }
})
* {
  font-family: Arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script></script>

<div id="app">
  <bs-select v-model="test" :options="options"></bs-select>
  <br><br>
  {{ test }}
  
  <br><br>
  
  <button @click="test = 'solved'">
  Set 'test' variable to 'solved'
  </button>
</div>
另一答案

对于现在遇到这种情况的人来说,这是让它只发射一次的正确方法。

<template>
    <select><slot></slot></select>
</template>
<script>
  module.exports = {
    props: ['options', 'value'],
    mounted: function () {
        console.log(this.options);
        var vm = this;
        $(this.$el)
        // init select2
        .select2({
            data: this.options,
            theme: 'bootstrap',
            width: '100%',
            placeholder: 'Select',
            allowClear: true
        })
        .val(this.value)
        .trigger('change')
        // emit event on change.
        .on('select2:select', function () { // bind to `select2:select` event
            vm.$emit('input', this.value)
        });
    },
    watch: {
        value: function (value) {
            // update value
            $(this.$el)
            .val(value)
            .trigger('change');
        },
        options: function (options) {
            // update options
            $(this.$el).empty().select2({ data: options })
        }
    },
    destroyed: function () {
        $(this.$el).off().select2('destroy')
    }
  }
</script>

以上是关于Vue组件$发射两次的主要内容,如果未能解决你的问题,请参考以下文章

vue,发射与传递函数作为道具

从子组件mounted()函数(Vue.js 2)列表中只收到一个发射[重复]

Vue.js - 从孙插槽组件发射没有将 $emit 事件冒泡到父级

Vue - 将数据从孩子传递给父母 - 基本发射不起作用

vue中的组件

我听不到从发射孩子到父母的交流