【问题标题】:Managing VueJs Watchers Dynamically动态管理 VueJs 观察者
【发布时间】:2017-11-18 19:07:45
【问题描述】:

我有一个包含多个字段的表格行的组件。经过 更新一个字段将使用基于保证金或售价的值更新另一个字段。

但是当我观察所有领域时,我得到了一个弹跳效果。添加 _debounce 有助于但不会阻止问题。为了尝试解决问题,我使用观察者的回调来触发 unwatch(),但是当我重新添加观察者时,回调会停止取消观察。

我有一个工作要点作为代码示例。

view example gist

Vue.component('pricing', {

  template: '#pricing-row',

  props: ['item'],

  mounted() {
    this.addWatchers()
  },

  methods: {

      resetWatchers() {
        setTimeout(()=> {
            this.addWatchers()
        }, 700)
    },

    addWatchers() {

      this.updateNet = this.$watch(
        function() {
            return this.item.net
        },
        function() {
            // unmount other watchers
            this.updateMargin()
            this.updateSell()
            // calculate sell price and update
            this.setSellPrice()
            // re-add watchers
            this.resetWatchers()
        }
      ),

      this.updateMargin = this.$watch(
        function() {
            return this.item.margin
        },
        function() {
          // unmount other watchers which can cause bounce effect
          this.updateSell()
          // calculate sell price and update
          this.setSellPrice()
          // re-add watchers
          this.resetWatchers()
        }
      ),

      this.updateSell = this.$watch(
        function() {
            return this.item.sell
        },
        function(sellPrice) {
          // unmount other watchers which can cause bounce effect
          this.updateMargin()
          // update margin
          this.setMargin(sellPrice)
          // re-add watchers
          this.resetWatchers()
        }
      )
    },

    setSellPrice() {
      let price = (100 / (100 - this.item.margin)) * this.item.net
      this.item.sell = price.toFixed(2)
    },

    setMargin(sellPrice) {
      let profit = (sellPrice - this.item.net)
      let price = (100 * profit) / sellPrice
      this.item.margin = price.toFixed(2)
    }
  }

})

new Vue({
  el: '#vue',
  data: {
    prices: [
        { 
        id: 1,
        net: 5,
        margin: 10,
        sell: 5.56
      },
        { 
        id: 2,
        net: 7,
        margin: 10,
        sell: 7.78
      },      
    ]
  }
})

我相信我通过将它们安装在mounted() 调用方法来正确使用观察者。并通过调用该方法重新初始化?

我真的希望你能帮上忙。

【问题讨论】:

  • 这似乎是非常不幸的解决方案。你能描述一下netmarginsell属性之间的关系吗?
  • 基本上通过更新售价,它会自动根据净额和保证金重新计算售价。其他字段相同。
  • 这是无限循环。现在我明白问题出在哪里了......最简单的解决方案是将 sell 属性视为原始价格,您不能直接使用,而作为实际售价,您必须使用基于净值、保证金和原始价格计算的属性。跨度>
  • 你考虑过用计算来代替手表吗?似乎是更优雅的解决方案。
  • 这也是同一个问题,Daniel.. 使用计算的 getter 和 setter 具有多个输入的相同副作用。

标签: javascript vue.js vuejs2 vue-component


【解决方案1】:

这是一个使用计算值的解决方案

每个参数(净额、保证金、卖出)都是通过计算值抽象出来的。 getter 返回 this.item 值,setter 先更新 this.item 值,然后更新相关值。

这是与颜色选择器类似的问题,您可以在此处查看此问题的更复杂版本http://jsfiddle.net/Phunky/2enc99r1/

Vue.component('pricing', {
	template: '#pricing-row',
  props: ['item'],
  computed: {
  	net:{
    	get () {
      	return Number(this.item.net)
      },
      set (net) {
      	this.item.net = Number(net)
        this.setSellPrice()
      }
    },
  	margin:{
    	get () {
      	return this.item.margin
      },
      set (margin) {
      	this.item.margin = Number(margin)
        this.setSellPrice()
      }
    },
  	sell:{
    	get () {
      	return this.item.sell
      },
      set (sell) {
      	this.item.sell = Number(sell)
      	this.setMargin()
      }
    }
  },
  methods: {
    
    setSellPrice() {
      let price = (100 / (100 - this.margin)) * this.net
      this.item.sell = price.toFixed(2)
    },
    
    setMargin() {
      let profit = (this.sell - this.net)
      let price = (100 * profit) / this.sell
      this.item.margin = Number(price.toFixed(2))
    }
  }
  
})

new Vue({
  el: '#vue',
  data: {
  	prices: [
    	{ 
      	id: 1,
        net: 5,
        margin: 10,
        sell: 5.56
      },
    	{ 
      	id: 2,
        net: 7,
        margin: 10,
        sell: 7.78
      },      
    ]
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.5/vue.min.js"></script>
<script type="text/x-template" id="pricing-row">
  <tr>
  	<td><input v-model="net" /></td>
    <td><input v-model="margin"  /></td>
    <td><input v-model="sell"  /></td>
  </tr>	
</script>


<div id="vue">
  <table>
    <tr>
      <th>Net</th>
      <th>Margin</th>
      <th>Price</th>
    </tr>
    <tr is="pricing" v-for="(price, index) in prices" :item="price" :key="price.id"></tr>
  </table>
</div>

【讨论】:

  • 我也这样做了。但我仍然得到弹跳效果。请参阅截屏视频。 d.pr/i/1l04rR
  • 好吧,在经历了这么多小时的挫折之后,我找到了路线的原因。我正在使用 bootstrap-vue 。看起来他们对输入的触发是均匀的。请给我一把枪! ! TY
  • 我尽量避免使用这些库,而不是像 picturepan2.github.io/spectre 这样的纯 css 框架,或者如果我需要添加的功能,然后使用专门为 vue 构建的 elemnet.io 之类的东西,以及不像 vue 的材料端口或引导程序那样修补。
猜你喜欢
  • 1970-01-01
  • 2018-12-11
  • 2019-04-22
  • 1970-01-01
  • 2018-04-18
  • 1970-01-01
  • 2021-10-20
  • 2018-01-03
  • 2020-05-22
相关资源
最近更新 更多