Vue - 如果添加或修改了对象,则深度观察对象数组的变化
Posted
技术标签:
【中文标题】Vue - 如果添加或修改了对象,则深度观察对象数组的变化【英文标题】:Vue - Deep watch change of array of objects, either if a object is added of modified 【发布时间】: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
);
<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 我的意思是,你对每辆车重复<car-component>
,而<car-component>
将呈现“你有......在购物车中”,所以你会有多条消息.我很困惑,因为您的问题表明您只想为最后一项显示一条消息,而不是为每项显示一条消息。
@Joe82 否则,如果你的代码是正确的,你就不需要深入观察,也不需要做你想做的事情。
【参考方案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[]
数组传递给<car-component>
,并允许组件确定cars[]
的哪个元素显示关于以下内容的消息:
在car-component
中,添加一个prop
(typed 以确保安全)来保存传入的cars[]
:
Vue.component('car-component',
// ...
props:
cars: Array
,
添加两个数据属性:
car
- 当前的汽车。
copyOfCars
- cars[]
的最后一个已知副本,用于确定哪个数组元素已更改。 注意:虽然为观察者提供了被观察属性的旧值和新值,但旧值实际上并不表示对象数组的先前值。
Vue.component('car-component',
//...
data()
return
car: ,
copyOfCars: undefined, // `undefined` because we don't need it to be reactive
;
,
定义一个方法(例如,命名为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;
在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
可以是任何东西。以上是关于Vue - 如果添加或修改了对象,则深度观察对象数组的变化的主要内容,如果未能解决你的问题,请参考以下文章
Vue子组件(deep)深度监听props对象属性无效的解决办法
vue深度响应Array中的Object(vue深度响应数组内的对象)