美文网首页vue学习
vue 数组更新视图不更新的问题

vue 数组更新视图不更新的问题

作者: 安石0 | 来源:发表于2018-10-24 17:57 被阅读0次

    0. 解决方案

    1.当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
    2.当你修改数组的长度时,例如:vm.items.length = newLength
    对于第一种情况,可以使用:Vue.set(example1.items, indexOfItem, newValue);而对于第二种情况,可以使用 vm.items.splice(newLength)。

    -----------------------原理浅析----------------------------分割线--

    1.demo

    我们实际中会用到的场景
    初始化的时候arr定义的是:[]
    源码中简化的原理,初始化的时候执行如下逻辑:

    data = {arr: []}
    val = []
    Object.defineProperty(data, 'arr', {
     enumerable: true,
     configurable: true,
     get: function () {
      console.log('你获取数组')
      return val
     },
     set: function(value) {
       console.log('你更新数组,我检测到了') 
       val = value
     }  
    })
    data.arr.forEach((v,i, array) => {
      var value = v
      Object.defineProperty(array, i, {
     enumerable: true,
     configurable: true,
     get: function () {
      console.log(`你在访问arr${i}`)
      return v
     },
     set: function(newVal) {
       console.log(`你在更新arr${i},我检测到了`) 
       v = newVal
     }  
    })
    })
    
    chrome中测试如下:
    data.arr
    VM485:7 你获取数组
    []
    VM485:7 你获取数组
    data.arr[1]
    VM485:7 你获取数组
    undefined
    VM485:7 你获取数组
    data.arr = [1,2,3]
    VM485:11 你更新数组,我检测到了
    (3) [1, 2, 3]
    VM485:7 你获取数组
    data.arr[1]
    VM485:7 你获取数组
    2
    data.arr[1] = 'new'
    VM485:7 你获取数组
    "new"
    VM485:7 你获取数组
    data.arr[2] = 'new2'
    VM485:7 你获取数组
    "new2"
    2VM485:7 你获取数组
    data.arr[1]
    VM485:7 你获取数组
    "new"
    VM485:7 你获取数组
    data.arr[3]
    VM485:7 你获取数组
    undefined
    data.arr[3]
    VM485:7 你获取数组
    undefined
    VM485:7 你获取数组
    data.arr.length = 100
    VM485:7 你获取数组
    

    可以发现当data.arr初始值为[],你直接改变data.arr的值是可以触发更新的,但是通过data.arr[index] = newVal的方式无法检测到变化,并且你直接通过
    splice等方法改变数组长度的方法都没有办法检测到更新

    data.arr.splice(0,1,'fake')
    VM485:7 你获取数组
    [1]
    data.arr
    VM485:7 你获取数组
    (100) ["fake", "new", "new2", empty × 97],
    

    2.vue中的处理

    1.当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
    2.当你修改数组的长度时,例如:vm.items.length = newLength
    对于第一种情况,可以使用:Vue.set(example1.items, indexOfItem, newValue);而对于第二种情况,可以使用 vm.items.splice(newLength)。
    其实通过splice的方式,在vue中是可以检测到更新的
    为什么呢?
    看一下源码:


    部分图解
    // vue-dev\src\core\util\lang.js
    def (obj, key,val,enumerable) {
      Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    })
    }
    // src/core/observer/array.js
    const arrayProto = Array.prototype
    export const arrayMethods = Object.create(arrayProto)
    
    const methodsToPatch = [
      'push',
      'pop',
      'shift',
      'unshift',
      'splice',
      'sort',
      'reverse'
    ]
    // 改造了列举的数组方法
    methodsToPatch.forEach(function (method) {
      // cache original method
     // 以push为例
      const original = arrayProto[method] // Array.prototype.push
    // def(arrayMethods,'push', function() {}) -> arrayMethods.push = function () {}  改造了push方法
    
      def(arrayMethods, method, function mutator (...args) {
        const result = original.apply(this, args) // this为data.arr
        const ob = this.__ob__
        let inserted
        switch (method) {
          case 'push':
          case 'unshift':
            inserted = args
            break
          case 'splice':
            inserted = args.slice(2)
            break
        }
        if (inserted) ob.observeArray(inserted)
        // notify change
        ob.dep.notify()// 派发更新了
        return result
      })
    })
    

    把data.arr.__proto __ = arrayMethods
    当我执行data.arr.push(xxx)的时候就会派发更新了,自然更新了视图

    相关文章

      网友评论

        本文标题:vue 数组更新视图不更新的问题

        本文链接:https://www.haomeiwen.com/subject/uexxtqtx.html