为什么我推荐Vue使用数组方法更新数据

77 阅读1分钟

我们可以用 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 都将得到更改后的值,因为对象是引用类型,指向同一个内存地址。