我们可以用 v-for
指令基于一个数组来渲染一个列表。
<ul id="example-1">
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
// items: [{ message: "Foo" }, { message: "Bar" }],
在 Vue2 中使用了 defineProperty 实现相应式,Vue 不能检测以下数组的变动,当你利用索引直接设置一个数组项时,例如:
this.items[1] = { message: "Bar2" }
但是当你直接修改数组中对象的属性,也将会触发视图更新。
// 方式1
this.items[1].message = 'Bar2'
但是我不推荐你这么做,官方的做法是使用数组的方法进行修改。
// 方式2
this.items.splice(1, 1, { message: "Bar2" })
为什么?当需要在数据变化时执行操作时,我们使用 watch 无法监听数组的变化。
// 方式1
<script>
export default {
data() {
return {
items: [{ message: "Foo" }, { message: "Bar" }],
};
},
methods: {
handleClick() {
// 直接修改属性
this.items[1].message = "Bar2";
},
},
watch: {
items(newVal, oldVal) {
// 👎 不会触发watch
console.log(newVal, oldVal);
},
},
};
</script>
当我们使用数组的方法,可以很方便的监听数组前后的变化。
// 方式2
<script>
export default {
data() {
return {
items: [{ message: "Foo" }, { message: "Bar" }],
};
},
methods: {
handleClick() {
// 数组方法修改
this.items.splice(1, 1, { message: "Bar2" });
},
},
watch: {
items(newVal, oldVal) {
// 可以出触发watch
console.log(newVal, oldVal);
},
},
};
</script>
如果要让方式1实现属性更改 watch 也能触发,可以通过 deep 参数来解决。
<script>
export default {
data() {
return {
items: [{ message: "Foo" }, { message: "Bar" }],
};
},
methods: {
handleClick() {
this.items[1].message = "Bar2";
},
},
watch: {
items: {
handler(newVal, oldVal) {
// newVal -> [{ message: "Foo" }, { message: "Bar2" }]
// oldVal -> [{ message: "Foo" }, { message: "Bar2" }]
console.log(newVal, oldVal);
},
deep: true,
},
},
};
</script>
缺点是,当你直接修改对象的属性,将无法得到对象更改前的值,此时 newVal 和 oldVal 都将得到更改后的值,因为对象是引用类型,指向同一个内存地址。