【问题标题】:Vue - listen props changes (object), watch not triggeringVue - 监听道具变化(对象),观看不触发
【发布时间】:2019-06-19 23:34:06
【问题描述】:

我有类似的问题: VueJs 2.0 - how to listen for `props` changes 但在我的情况下,我将对象从父级发送到子级,同时传递了几次以使用 JS 中对象引用的好处。

有一个例子,但工作正常:

<div id="app">
  <child :myprop="myObj"></child>
  <button @click="text = 'Another text'">Change text</button>
</div>

<script>
new Vue({
  el: '#app',
  data: {
    myObj: {
       id: null
    }
  },
  components: {
    'child' : {
      template: `<div>
      <p>{{ myprop.id }}</p>
      <select v-model="myprop.id">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
      </select>
      </div>`,
      props: ['myprop'],
      watch: { 
        'myprop.id': function(newVal, oldVal) { // watch it
          console.log('Prop changed: ', newVal, ' | was: ', oldVal)
        }
      }
    }
  }
});
</script>

https://jsfiddle.net/8hqwg6Ln/

在我的例子中,我将 v-model on select 设置为“parentObj.id”,然后为此添加观察者。目标是在更改 id 后更改其他 parentObj 属性 - 用户选择 id,然后我在我的数组中搜索此 id 并将 parentObj.price 设置为与选择字段相同级别的其他组件(修改输入)的 v-model .

当我改变选择时,观察者不会被触发。如果我用我的自定义方法添加@change,它可以工作,但有延迟(我在 vue 开发工具中看到了这个,必须刷新才能获取当前数据),而且,它不适用于第二个输入组件 - 它看不到更改 parentObj.price。

使用复杂集合并向下传递和修改具有许多不同组件的集合的最佳方法是什么? 我认为,我可以将属性的“副本”添加到本地级别(因此,创建具有 id 和孩子价格的数据),添加观察者,并使用 this.$set 更新收到的道具。但是......这是一个不错的选择,也许这里有更好的选择?

【问题讨论】:

    标签: javascript vue.js vuejs2 vue-component pass-by-reference


    【解决方案1】:

    这很容易。你需要添加 deep 属性来观察对象。

    props: ['myprop'],
      watch: { 
        myprop: {
            deep: true,
            handler: function(newVal, oldVal){
               console.log('Prop changed: ', newVal, ' | was: ', oldVal)
            }
          }
        }
      }
    

    你不需要使用类似的东西

    'myprop.id'
    

    【讨论】:

    • 这对我有用 - 我编辑了代码 sn-p 以修复语法。
    【解决方案2】:

    这样试试

    {
      ...
      props: ['myprop'],
      watch: { 
        'myprop.id': {
          handler(newVal, oldVal) {
            console.log('Prop changed: ', newVal, ' | was: ', oldVal)
          },
          deep: true
        }
      ...
    }

    【讨论】:

    • 不幸的是,不起作用。同样使用 immediate as true 仍然不起作用。
    【解决方案3】:

    你的代码只有一个问题:

    "text = 'Another text'"
    

    不起作用,因为未声明文本。所以你可能想把它改成

    "myObj = { id: 'Another text' }"
    

    我想这就是你想要的。然后它是反应性的,当你选择时文本变为 1 或 2 或 3,当你点击时变为“另一个文本”,你的观察者触发并记录旧值和新值

    【讨论】:

    • 此文本仅从其他示例中粘贴,抱歉。问题出在 myObj.id 并使用子组件中的 select 更改它。
    • 我明白了,也许你应该考虑使用 Vuex,(或者,如果你认为 Vuex 过分杀伤力,或者只是一个作为数据而不是 prop 传递给组件的对象)。然后每个组件都可以更改共享状态而不依赖于父级。见vuejs.org/v2/guide/state-management.html(它不会创建副本,顺便说一下,它是“多个组件使用的引用”)
    【解决方案4】:

    首先需要一个用于父组件的 prop 和一个用于选择 v-model 的本地数据:

    <select v-model="localid">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
          </select>
    

    你的子组件的 prop 和 data 是:

    data() {
          return {
            localid:null
          }
        },
    props: ['pid']
    

    如果组件道具发生了变化并且他的父母想知道这个变化,你可以使用.sync修饰符。首先更新组件中的道具。观察你的 localid 数据,然后用你的新数据更新你的 pid 属性:

    watch:{
         localid:function(){
         this.$emit('update:pid', this.localid)
        }
    }
    

    现在 parent 知道 pid prop 已更改,并且您的父组件的数据将随着 prop 的新数据而更改。你的父组件:

    <div id="app">
      <child :pid="myObj.id"></child>
      <button @click="text = 'Another text'">Change text</button>
    </div>
    

    更多信息:https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier

    【讨论】:

      【解决方案5】:

      我测试了一些东西,发现了错误和解决方案。首先,我的自定义输入中有一个错误——它是我的组件,它在里面使用了 cleave-vue。 Cleave-vue 有 v-model,我也在我的自定义组件上使用了 v-model,但这是错误的 - 我必须使用带同步的值绑定(例如::value.sync="parentCost")。

      第二件事:是的,我必须在本地创建值,然后将其传递给孩子,并添加观察者。在观察者中,我进行了额外的操作,如 parseInt/parseFloat 等并设置父数据。现在一切正常。这是一些数据重复,但我认为不是大问题 - 我创建了“抽象”mixin,因为我有很多类似的自定义组件,并且每个都使用观察者/设置者模板。代码很干净。

      【讨论】:

        【解决方案6】:

        您应该能够通过简单地使用“$emit”将子属性“myprop”的值发送到父应用。

        new Vue({
          el: '#app',
          data: {
            myObj: {
               id: null
            }
          },
          components: {
            'child' : {
              template: `<div>
              <p>{{ myprop.id }}</p>
              <select v-model="myprop.id" @change="$emit('update', myprop);">
                <option value="1">1</option>
                <option value="2">2</option>
                <option value="3">3</option>
              </select>
              </div>`,
              props: ['myprop'],
              watch: { 
                'myprop.id': function(newVal, oldVal) { // watch it
                  console.log('Prop changed: ', newVal, ' | was: ', oldVal)
                }
              }
            }
          }
        });
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
        <div id="app">
          <child :myprop="myObj"></child>
          reading from child: {{myObj.id}}
        </div>

        【讨论】:

          猜你喜欢
          • 2018-10-12
          • 2018-01-15
          • 2017-11-18
          • 2018-12-24
          • 2019-11-16
          • 2011-10-05
          • 2020-03-26
          • 1970-01-01
          • 2021-01-12
          相关资源
          最近更新 更多