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

时间:2018-09-20 16:53:22

标签: javascript vue.js synchronization parent-child prop

使用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

1 个答案:

答案 0 :(得分:1)

让我们看一下您的示例。我为父组件和子组件以及bar语句的console.log属性值添加了监视程序,以注意两个组件之间数据传输的不同点:

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,然后触发用于计算的get的{​​{1}}和set方法。但是,_bar函数完成之前,bar的两个观察程序不会触发。这表明直到handler_button_click函数执行完毕后,父级组件才处理子级$emit调用传递的值。

Vue提供的等待handler_button_click函数中父级和子级属性同步的唯一方法是调用handler_button_click,如上所述。 $nextTick函数将等待执行其处理程序,直到DOM完成更新为止。由于DOM必须等到父组件和子组件中的所有数据更改都解决后才能完成渲染,因此可以确保此时所有内容都将保持同步。


所有这些,似乎您只是希望孩子的$nextTick属性在设置后立即更新,而不必一定要立即在父范围中更新该值。

在这种情况下,您可以使用观察程序来代替计算的getter和setter。这样,由于不计算孩子的财产,它将立即更新,而父母的财产将在下一个滴答之后更新。

这是一个例子:

_bar
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,因为不能将带有下划线的属性视为they are not proxied to the Vue instance