Vue.js - 道具同步不是即时的

Vue.js - prop sync not instant

使用vue.js 在父子之间同步道具。问题是同步使用事件,每次我更改值时,我都必须在值更新之前等待 $nextTick。这并不理想,因为我不想在每次更改值时都输入 $nextTick。有没有办法让事件/道具更新立即发生?

HTML:

<div id="app">
    <foo inline-template v-bind:bar.sync="bar">
        <div>
            <button v-on:click="handler_button_click">Set to 5</button>
        </div>
    </foo>
    <span>bar: {{bar}}</span>
</div>

JS:

const EVENT_UPDATE_BAR = "update:bar";

Vue.component("foo", {
    props:["bar"],
    computed:{
        _bar:{
            get:function(){
                return this.bar;
            },
            set:function(value){
                //Mutating the prop here solves the problem, but then I get a warning about mutating the prop...
                //this.bar = value;
                this.$emit(EVENT_UPDATE_BAR, value);
            }
        }
    },
    methods:{
        handler_button_click:function(){
            //It seems that $nextTick must run before value is updated
            this._bar = 5;
            //This shows old value - event / prop has not fully propagated back down to child
            alert("bar: " + this._bar);
        }
    }
});

new Vue({
    el:"#app",

    data:{
        bar:1
    }
});

参见 CodePen 上的工作示例:https://codepen.io/koga73/pen/MqLBXg

让我们来看看你的例子。我为 parent 和 child 组件和 console.log 语句的 bar 属性 的值添加了观察者,以注意传输中的不同点两个组件之间的数据:

const EVENT_UPDATE_BAR = "update:bar";

Vue.component("foo", {
  props:["bar"],
  computed:{
    _bar:{
      get:function(){
          console.log('_bar getter is called')
        return this.bar;
      },
      set:function(value){
          console.log('_bar setter is called')
        this.$emit(EVENT_UPDATE_BAR, value);
      }
    }
  },
  methods:{
    handler_button_click:function(){
      console.log('handler_button_click is called')
      this._bar = 5;
      console.log('this._bar is accessed with value: ', this._bar);
      this.$nextTick(() => {
        console.log('next tick handler is called')   
      })
      console.log('handler_button_click finishes')
    }
  },
  watch: {
    bar() {
      console.log('child bar watcher is called')
    }
  }
});

new Vue({
  el:"#app",
  
  data:{
    bar:1
  },
  watch: {
    bar() {
      console.log('parent bar watcher is called')
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <foo inline-template v-bind:bar.sync="bar">
    <div>
      <button v-on:click="handler_button_click">Set to 5</button>
    </div>
  </foo>
  <span>bar: {{bar}}</span>
</div>

您会注意到 handler_button_click 首先触发,然后是计算 _bargetset 方法。但是, bar 的两个观察者直到 handler_button_click 函数完成后才会触发。这表明 child 的 $emit 调用传递的值在 handler_button_click 函数完成执行之前不会被 parent 组件处理。

Vue 提供的等待 parent 和 child 的属性在 handler_button_click 函数内同步的唯一方法是调用 $nextTick,正如你所描述的。 $nextTick 函数将等待执行其处理程序,直到 DOM 完成更新。由于 DOM 在 parent 和 child 组件中的所有数据更改都得到解决之前不会完成渲染,因此您可以确定此时一切都会同步。


综上所述,您似乎只是想让 child 的 _bar 属性 在设置后立即更新,您不一定需要立即在 parent 范围内更新值。

在那种情况下,您可以使用观察者而不是计算的 getter 和 setter。这样,由于未计算 child 的 属性,它将立即更新,而 parent 的 属性 将在下一次报价后更新。

这是一个例子:

const EVENT_UPDATE_BAR = "update:bar";

Vue.component("foo", {
  props: ["bar"],
  data() {
    return {
      value: this.bar,
    };
  },
  methods: {
    handler_button_click() {
      this.value = 5;
      alert("bar: " + this.value);
    }
  },
  watch: {
    value(val) {
      this.$emit(EVENT_UPDATE_BAR, val);
    },
    bar(val) {
      this.value = val;
    }
  }
});

new Vue({
  el: "#app",
  data() {
    return { 
      bar: 1
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <foo inline-template v-bind:bar.sync="bar">
    <div>
      <button v-on:click="handler_button_click">Set to 5</button>
    </div>
  </foo>
  <span>bar: {{bar}}</span>
</div>

请注意,我已将 _bar 属性 的名称更改为 value,因为无法将带有下划线的属性视为 they are not proxied to the Vue instance