【问题标题】:Vue - Deep watch change of array of objects, either if a object is added of modifiedVue - 如果添加或修改了对象,则深度观察对象数组的变化
【发布时间】:2019-02-18 05:08:21
【问题描述】:

我正在尝试在 vue.js 中创建一个元素,这样当我更新我的购物车时,它会显示一个警告,其中包含已添加/更新到购物车的项目。因此,如果我添加一辆新车,它将显示最后添加的汽车。

cars: [
  { name: 'Porsche', quantity: 2},
  { name: 'Ferrari', quantity: 1},
  { name: 'Toyota', quantity: 3}
]

cars: [
  { name: 'Porsche', quantity: 2},
  { name: 'Ferrari', quantity: 1},
  { name: 'Toyota', quantity: 3},
  { name: 'Mustang', quantity: 1}
]

会显示

<div>
You have 1 x Mustang in Cart
</div>

但是,如果我更新已经在购物车中的汽车的数量,它将显示最后一辆车已更新。

cars: [
  { name: 'Porsche', quantity: 2},
  { name: 'Ferrari', quantity: 1},
  { name: 'Toyota', quantity: 3}
]

cars: [
  { name: 'Porsche', quantity: 2},
  { name: 'Ferrari', quantity: 1},
  { name: 'Toyota', quantity: 4}
]

会显示

<div>
You have 4 x Toyota in Cart
</div>

到目前为止,我已经在 this answer 上运行它

new Vue({
el: '#app',
data: {
    cars: [
      { name: 'Porsche', quantity: 2},
      { name: 'Ferrari', quantity: 1},
      { name: 'Toyota', quantity: 3}
    ]
}
});

Vue.component('car-component', {
props: ["car"],
data: function() {
  return {
    lastAdded:''
  }
},
template: `
  <div>
    You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
  </div>`,

watch: {
  car: {
    handler: function(newValue) {
       this.lastAdded = newValue;
    },
      deep: true
  }
}
});

html

<script src="https://unpkg.com/vue@2.5.17/dist/vue.js"></script>
<body>
  <div id="app">
    <p>Added to Cart:</p>
    <car-component :car="car" v-for="car in cars"></car-component>
  </div>
</body>

关键是现在它只检测一个物体何时已经在购物车中并改变数量,而不是当有一辆新车添加时。我试图和另一个观察者一起玩,但是没有用。提前致谢!

【问题讨论】:

  • 这不会显示每辆车的警告信息吗?我以为您只希望它显示在购物车中添加/更新的最后一项。
  • Vue 在跟踪对象和数组时需要注意一些注意事项。看看这个:vuejs.org/v2/guide/list.html#Caveats
  • @DecadeMoon 我编辑了 html,它应该显示 lastItem 值
  • @Joe82 我的意思是,你对每辆车重复&lt;car-component&gt;,而&lt;car-component&gt; 将呈现“你有......在购物车中”,所以你会有多条消息.我很困惑,因为您的问题表明您只想为最后一项显示一条消息,而不是为每项显示一条消息。
  • @Joe82 否则,如果你的代码是正确的,你就不需要深入观察,也不需要做你想做的事情。

标签: vue.js vue-component


【解决方案1】:

嗯,我该怎么做?

在我看来,我们有一个对象数组,我们正在跟踪最近添加或修改的对象。当然。

所以,我想我只想跟踪最近修改的对象并渲染它。

首先是html:

<div id="app">
    <p>Added to Cart:</p>
    <car-component :car="latestCar"></car-component>
</div>

和 vue 实例:

new Vue({
el: '#app',
data: {
    cars: [
      { name: 'Porsche', quantity: 2},
      { name: 'Ferrari', quantity: 1},
      { name: 'Toyota', quantity: 3}
    ],
    latestCar: {}
},
methods: {
  updateLatestCar(car) {
     this.latestCar = car;
     //call this method from any other method where updates take place
     //so if would be called from your addCar method and your updateCar method
     //(which I assume exist even though they are not shown in your code)        
  }
}
});

Vue.component('car-component', {
props: ["car"],
data: function() {
  return {
    lastAdded:''
  }
},
template: `
  <div>
    You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
  </div>`,

watch: {
  car: {
    handler: function(newValue) {
       this.lastAdded = newValue;
    },
      deep: true
  }
}
});

如果您通过 Vue 实例外部的某种方法修改对象数组,则需要额外考虑。

但看起来你在 Vue 实例方法块中有一些方法,如下所示:

addCar(car) {
  this.cars.push(car);
  this.updateLatestCar(car);
},
updateCar(index, car) {
  this.cars[index] = car;
  this.updateLatestCar(car);
}

【讨论】:

  • 感谢您的回答!我想过这样的事情,但我宁愿和观察者一起解决它。关键是,我有几种方法可以更新存储在 vuex 中的对象数组,并且必须为每个对象添加此更新方法似乎不是最佳解决方案。
【解决方案2】:

您可以将整个cars[] 数组传递给&lt;car-component&gt;,并允许组件确定cars[] 的哪个元素显示关于以下内容的消息:

  1. car-component 中,添加一个proptyped 以确保安全)来保存传入的cars[]

    Vue.component('car-component', {
      // ...
      props: {
        cars: Array
      },
    }
    
  2. 添加两个数据属性:

    • car - 当前的汽车。
    • copyOfCars - cars[] 的最后一个已知副本,用于确定哪个数组元素已更改。 注意:虽然为观察者提供了被观察属性的旧值和新值,但旧值实际上并不表示对象数组的先前值。

      Vue.component('car-component', {
        //...
        data() {
          return {
            car: {},
            copyOfCars: undefined, // `undefined` because we don't need it to be reactive
          };
        },
      }
      
  3. 定义一个方法(例如,命名为findActiveCar)来确定给定cars[] 中的哪个元素是最近“活动的”(新添加或修改的)。

    Vue.component('car-component', {
      // ...
      methods: {
        /**
         * Gets the newest/modified car from the given cars
         */
        findActiveCar(newCars) {
          if (!newCars || newCars.length === 0) return {};
    
          let oldCars = this.copyOfCars;
    
          // Assume the last item of `newCars` is the most recently active
          let car = newCars[newCars.length - 1];
    
          // Search `newCars` for a car that doesn't match its last copy in `oldCars`
          if (oldCars) {
            for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
              if (newCars[i].name !== oldCars[i].name
                  || newCars[i].quantity !== oldCars[i].quantity) {
                car = newCars[i];
                break;
              }
            }
          }
    
          this.copyOfCars = JSON.parse(JSON.stringify(newCars));
          return car;
        }
      }
    }
    
  4. cars 属性上定义一个watcher,将car 设置为来自findActiveCar() 的新/修改项。

    Vue.component('car-component', {
      // ...
      watch: {
        cars: {
          handler(newCars) {
            this.car = this.findActiveCar(newCars);
          },
          deep: true, // watch subproperties of array elements
          immediate: true,  // run watcher immediately on `this.cars[]`
        }
      },
    }
    

Vue.component('car-component', {
  props: {
    cars: Array,
  },
  data() {
    return {
      car: {},
      copyOfCars: undefined,
    }
  },
  template: `<div>You have {{car.quantity}} x {{car.name}} in Cart</div>`,

  watch: {
    cars: {
      handler(newCars) {
        this.car = this.findActiveCar(newCars);
      },
      deep: true,
      immediate: true,
    }
  },

  methods: {
    findActiveCar(newCars) {
      if (!newCars || newCars.length === 0) return {};

      let oldCars = this.copyOfCars;
      let car = newCars[newCars.length - 1];

      if (oldCars) {
        for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
          if (newCars[i].name !== oldCars[i].name
              || newCars[i].quantity !== oldCars[i].quantity) {
            car = newCars[i];
            break;
          }
        }
      }

      this.copyOfCars = JSON.parse(JSON.stringify(newCars));
      return car;
    }
  }
});


new Vue({
  el: '#app',
  data: () => ({
    cars: [
      { name: 'Porsche', quantity: 2},
      { name: 'Ferrari', quantity: 1},
      { name: 'Toyota', quantity: 3}
    ]
  }),
  methods: {
    addCar() {
      this.cars.push({
        name: 'Mustang', quantity: 1
      })
    }
  }
})
<script src="https://unpkg.com/vue@2.5.17"></script>

<div id="app">
  <h1>Added to Cart</h1>
  <button @click="addCar">Add car</button>
  <ul>
    <li v-for="(car, index) in cars" :key="car.name + index">
      <span>{{car.name}} ({{car.quantity}})</span>
      <button @click="car.quantity++">+</button>
    </li>
  </ul>
  <car-component :cars="cars" />
</div>

【讨论】:

  • 效果很好!只有一个问题:props: {cars: Array} 部分是做什么的,与props:['cars'] 有什么不同?谢谢!
  • Vue 允许您specify the expected type of a prop。因此,props: {cars: Array} 告诉 Vue 如果提供的 cars 实际上不是一个数组,则发出错误。另一方面,props: ['cars'] 没有类型检查,cars 可以是任何东西。
猜你喜欢
  • 2017-04-29
  • 1970-01-01
  • 2018-07-12
  • 2021-01-12
  • 1970-01-01
  • 1970-01-01
  • 2019-01-08
  • 2015-07-19
  • 2023-04-02
相关资源
最近更新 更多