Vue当中数组更新检测不到的问题怎么解决?从数组检测机制出发

275 阅读2分钟
先说结论:使用vm.$set()实例方法强制更新数组,

数组里面传三个参数:

1.目标数组 2.更新位置(数组索引) 3.更新的值

例如:vm.$set(arr,0,1)意思是将arr数组中下标为0的元素的值更改为1,更改之后强制更新数组.

结论不是重点!重点是明白为什么vue会出现这种情况,从而对Vue的数组检测机制有更清楚的理解

在Vue当中,利用数组数据去渲染页面结构是再寻常不过的事情了.

为什么有时候在Vue当中会出现数组更新检测不到的情况呢?

回答这个问题需要从Vue的数组检测机制讲起.

其实.......vue是不能检测数组和对象变化的......这个是官网原话的截图

image.png

所以,如果要想搞清楚底层原理的话,也许这个问题应该先换一种问法:

为什么vue明明不能检测数组变化,但是却可以做到根据数组变化来响应页面的效果呢?

因为实际上,vue不是通过直接检测数组变化来响应页面的,而是通过对调用数组方法,数组重新赋值等操作进行检测从而间接检测数组变化.当这些特定的操作发生的时候,vue就会认为数组发生变化了,从而响应页面.

这可以大致分为两种情况:

1.这些方法会改变数组, vue检测到这些改变数组的方法,直接更新页面

arr.push(),arr.pop(),arr.splice(),arr.reverse(),arr.unshift(),arr.sort()......

2.这些方法不会触发改变数组,而是会得到新数组.不会导致vue更新页面.需要手动把新数组赋值给老数组页面才会更新

arr.filter(),arr.slice(),arr.map(),arr.concat()......

3.vue目前检测不了,无法响应页面的情况

3.1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

3.2 当你修改数组的长度时,例如:vm.items.length = newLength

遇到这两种情况可以参考官方文档解决.但是最简单的方法是开头介绍的vm.$set()实例方法

具体代码如下:

<template>
  <div>
    <ul>
      <li v-for="item in list" :key="item">
        {{ item }}
      </li>
    </ul>
    <button @click="revBtn">数组翻转</button>
    <button @click="sliceBtn">截取前2个</button>
    <button @click="updateBtn">更新第一个元素值</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [10,20,30,40,50]
    }
  },
 methods:{
                revBtn() {
                     // 1. 数组翻转可以让v-for更新 -->
                    this.list.reverse()
                },
                sliceBtn() {
                    // 2. 数组slice方法不会造成v-for更新
                    /* 原因:slice没有改变原始数组,只是从原始数组中获取元素
                       解决方案: 覆盖原始数组
                    */
                    let newArr = this.list.slice(0, 2)
                    this.list = newArr
                },
                updateBtn() {
                    // 3. 替换元素值,不会造成v-for更新
                    // this.list[0] = 88

                    /* 解决方案: this.$set()
                    参数1: 更新目标结构
                    参数2: 更新位置
                    参数3: 更新值
                    */
                    this.$set(this.list, 0, 88)
                }
 },
}
</script>