shizhihuaxu / study-notes

学习笔记,见 issues
0 stars 0 forks source link

vue 数据变化检测的问题 #24

Open shizhihuaxu opened 3 years ago

shizhihuaxu commented 3 years ago

1、vue 是如何实现响应式的?(数据变化触发视图更新) 2、哪些情况下不能在数据变化后触发视图更新?为什么? 3、如何解决上述问题 2 ,原理又是什么? 4、为什么数组push 、shift 等一些操作可以触发视图更新?

shizhihuaxu commented 3 years ago

vue 是如何实现响应式的?(数据变化触发视图更新)

​ 把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter,做依赖收集和派发更新;每个组件实例都对应一个 Watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染;

设置响应式属性的处理方式

​ 初始化数据时会层层判断 data,props property 对应的 value 是不是对象(一个数组或者纯对象),如果是的话需要将 value 的属性也处理为响应式属性;这里会有两种情况:

​ 一、如果 value 是一个纯对象的话,直接将此对象的 property 转为 getter/setter;

​ 二、如果 value 是一个数组的话,是对数组的每个元素进行处理,并没有将数组索引定义为响应式属性(因此利用索引直接设置一个数组项不会触发视图的更新)元素不是对象则不做处理,若每个元素为对象,则将元素每个属性定义为响应式属性;

不能检测变化的情况

  1. 初始化实例时属性未定义在 data 选项中

    var vm = new Vue({
     data:{
       a:1
     }
    })
    
    vm.a // 响应式
    
    vm.b  // 初始化后再添加直接属性,非响应式

    解决方案:

    Vue.set ,this.$set

  2. 对象新增、删除属性

    <div>{{ obj.age }}</div>
    <div>{{ obj.name }}</div>
    
    data() {
    return {
        obj: {
            age: 32
        }
    }
    }
    
    change() {
       delete this.obj.age // 删除属性
       connsole.log(this.obj.age) // undefined
    
       this.obj.name = 'lisi'  // 新增属性
       connsole.log(this.obj.name) // lisi
    }

    解决方案:直设置整个对象的值

    this.obj = {
    name: 'lisi'
    }
  3. 利用索引直接设置一个数组项

    <div>{{ arr1[1] }}</div>
    <div>{{ arr2[0].age }}</div>
    
    data() {
    return {
           arr1: [1,2,3], // 数组情况1
        arr2: [  // 数组情况 2
            {
                age: 12,
            },
            {
                age: 11,
            },
        ],
    }
    }
    
    change() {
       this.arr1[1] = 777 // 示例 1
    
    this.arr2[0] = {  // 示例 2
        age: 442
    }
    }

    解决方案:

    1、Vue.set ,this.$set
    2、借助数组操作方法
       'push',
       'pop',
       'shift',
       'unshift',
       'splice',
       'sort',
       'reverse'
    
    3、this.arr2[0].age = 442
  4. 直接修改数组的长度

    <div>{{ arr.length }}</div>  // 3
    
    data() {
    return {
        arr: [1,2,3]
    }
    }
    
    change() {
       this.arr.length = 888
    }

    解决方案:

    借助数组方法:
       'push',
       'pop',
       'shift',
       'unshift',
       'splice',
       'sort',
       'reverse'

为什么数组push 、shift 等一些操作可以触发视图更新?

​ vue 基于原生Array的原型对象创建了一个新对象,重新定义了数组中的以下方法,将以上方法定义通过defineProperty 定义成了响应式,参见 core/observer/array。

'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'