从父组件更改子组件检查状态同步失败

时间:2018-04-19 12:10:54

标签: javascript vue.js vue-component

请运行工作演示:



let myCheckbox = Vue.component('my-checkbox', {
  template: `<div>
                <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
            </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  }
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++
        this.checked = isChecked
      if (this.count % 2 === 0) {
        // this.checked = !isChecked
        setTimeout(() => {
          this.checked = !isChecked
        }, 10);
      }
    }
  },
  components: {
    myCheckbox
  }
})
&#13;
span {
  background: pink;
}
&#13;
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even your check will fail
</div>
&#13;
&#13;
&#13;

但是,如果我将同步操作更改为change中的异步操作,这意味着更改

        setTimeout(() => {
          this.checked = !isChecked
        }, 10);

         this.checked = !isChecked

该演示无法正常运行,因为checked中的check1未更新。

我的问题是

我不知道为什么同步操作不起作用!

我想在promise之后有一个change进行基础操作,这就是为什么setTimeout有效。但是,我不确定,我没有&#39}找不到任何解释。

1 个答案:

答案 0 :(得分:2)

当勾选复选框时,(原生DOM)复选框会通过添加v(刻度符号)来更改其外观。

当该滴答发生时,它会发出change事件。

请注意,此时,Vue的checked属性尚未更改。当“某人”选择change事件并相应地设置checked时,就会发生此更改。这就是下面的第4行:

methods: {                      // 1
  change(isChecked) {           // 2
    this.count++;               // 3
    this.checked = isChecked    // 4

现在,当this.count为偶数时,在代码没有 setTimeout的情况下,您立即将this.checked设置为其之前的值。

因此,由于Vue发现this.checked在执行方法后没有改变,因此Vue知道没有更新(没有重新绘制)。这就是为什么勾选的符号(上面v)仍然(错误地)勾选:Vue没有重新绘制该组件。

作为证明,请尝试下面的演示。请注意,updated生命周期处理程序永远不会执行:

let myCheckbox = Vue.component('my-checkbox', {
  template:
  `<div>
     <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
   </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  },
  // not executed because `checked` never changes (is always true)
  updated() { console.log('my-checkbox updated') }
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++;
      this.checked = isChecked
      if (this.count % 2 === 0) {
        this.checked = !isChecked
      }
    }
  },
  components: {
    myCheckbox
  }
})
span {
  background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even your check will fail. - checked: <span>{{checked}}</span> - it never changes, always <b>true</b>, so my-checkbox <b>never</b> has to be updated/repainted.
</div>


现在,当您使用setTimeout时,change(isChecked)方法已结束执行,this.checked 已更改!这会引发重画。

问题是setTimeout处理程序执行后不久,将this.checked恢复为原始值。这会触发另一次重绘。请注意,在下面的演示中,当this.count为偶数时,更新的挂钩执行两次

最后,作为最后一点,在Vue中这样做的惯用方法是using Vue.nextTick(),而不是setTimeout

let myCheckbox = Vue.component('my-checkbox', {
  template:
  `<div>
     <input type="checkbox" id="check1" :checked="checked" @change="change">{{checked}}            
   </div>`,
  props: ['checked'],
  methods: {
    change() {
      this.$emit('change', event.target.checked);
    }
  },
  updated() { console.log('my-checkbox updated') },
})

new Vue({
  el: '#app',
  data: {
    checked: true,
    count: 1
  },
  methods: {
    change(isChecked) {
      this.count++;
      this.checked = isChecked
      if (this.count % 2 === 0) {
        Vue.nextTick(() => {
          this.checked = !isChecked
        });
        //setTimeout(() => {
        //  this.checked = !isChecked
        //}, 10);
      }
    }
  },
  components: {
    myCheckbox
  }
})
span {
  background: pink;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <my-checkbox :checked="checked" @change="change"></my-checkbox>
  When
  <span>{{count}}</span> is even the checked will be overridden. checked: <span>{{checked}}</span>
</div>