关于Vue2.x中数组的劫持,我提了下面几个问题

你好,我是终身学习的阿飞。

我做了个青柠檬读书会的公众号,每天分享我的学习、读书的内容,同时也会分享我的一些学习方式和一些软件推荐。

我最近在读《深度工作》,有点相见很晚的感觉。预计在下周会写一个读后感,发表在青柠檬读书会的公众号。欢迎你前来阅读

如果您有任何问题,可以在博客下方留言,我们一起探讨。

有兴趣可以关注,我们一起进步!


在昨天的文章探讨vue2.x的数据劫持是怎么实现的?中,我们探讨了vue是如何对对象劫持的。今天继续探讨另外一个问题。

vue中是如何对数组进行劫持的。

在使用vue的过程中,我们知道以下几点:

  • 通过index对数组项进行改变,vue无法监听

  • 通过.length操作数组,vue无法监听

  • vue中对数组操作需要使用变异方法

    • push
    • pop
    • shift
    • unshift
    • sort
    • revers
    • splice
  • 数组中的对象发生变化,是可以被监听的,比如 arr[1].a = 1

  • 直接替换数组,可以被监听

我们今天探讨的就是在vue中,这些劫持是如何实现的。对数组的操作,又为什么会有这两个缺陷呢?

在vue对data进行监听的时候,如果发现需要监听的对象是个数组…

如果发现需要监听的对象是个数组,那么就不能通过defineProperty来对数据进行监听了。

如果是数组,就需要对数组的原方法进行重写。也就是所谓的变异方法。

class Observer {
    constructor(data) {
        if (Array.isArray(data)) {
        // 注意这里的方法
           data.__proto__ = arrayMethods
        } else {
            this.walk(data)
        }
    }
    walk(data) {
        // 对非数组的值进行劫持
    }
}

我们新建一个array.js, 用于对数组进行处理

let oldArrayProtoMtthods = Array.prototype
export let arrayMethods = Object.create(oldArrayProtoMtthods)
let methods = [
    'push',
    'pop',
    'shift',
    'unshift',
    'reverse',
    'sort',
    'splice'
]
methods.forEach(m => {
    arrayMethods[m] = function (...args) {
        let r = oldArrayProtoMtthods[m].apply(this, args)
        let inserted
        switch (m) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.slice(2)
                break;
        }
        if(inserted) observerArray(inserted)
        console.log('调用了数组的更新方法')
        return r
    }
})

接下来对这段代码逐段分析:

let oldArrayProtoMtthods = Array.prototype
export let arrayMethods = Object.create(oldArrayProtoMtthods)

这里通过Object.create重新得到一个新的对象,这个对象包含Arrar的所有方法。

Object.create(proto, [propertiesObject])
这个方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。

之所以这样做,是为了不影响「VUE」以外的数组方法。

let methods = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice' ]
methods.forEach(m => {
    arrayMethods[m] = function (...args) {
        let r = oldArrayProtoMtthods[m].apply(this, args)
    }
})

这里,对新的arrayMethods(就是赋值的新对对象,包含所有的原数组上面的方法)上的七个需要变异的方法进行处理.

oldArrayProtoMtthods[m].apply(this, args)
  • 这里的apply是改变this的指向,如果没有修改,则this指向的是Array.prototype,修改之后,this指向的是调用方。比如:arr.push(a), this指向的就是arr
  • args指的是传入的数据,是一个数组

如果变异方法中,对数组添加对象

我们在导读里面说:数组中的对象发生变化,是可以被监听的,比如 arr[1].a = 1

当我们在使用变异方法去修改数组的时候,假如给数组添加了一个对象,这里面的对象是没有被监听的。这里需要怎么处理呢?

methods.forEach(m => {
    arrayMethods[m] = function (...args) {
        let r = oldArrayProtoMtthods[m].apply(this, args)
        let inserted
        switch (m) {
            case 'push':
            case 'unshift':
                inserted = args
                break;
            case 'splice':
                inserted = args.slice(2)
                break;
        }
        if(inserted) observerArray(inserted)
        console.log('调用了数组的更新方法')
        return r
    }
})

在这里,定义一个inserted的变量。

七个变异方法中,只有三个方法可以向数组插入值。他们分别是:pushunshiftsplice

let inserted
switch (m) {
    case 'push':
    case 'unshift':
        inserted = args
        break;
    case 'splice':
        inserted = args.slice(2)
        break;
}
if(inserted) observerArray(inserted)

如果监听到数组被插入数据,即inserted有值,则对inserted进行循环,然后对每一项进行监听。

export function observerArray (inserted) {
    console.log('inserted', inserted)
    // 循环数组,对数组的每一项进行观测
    for(let i = 0; i < inserted.length; i++) {
        observer(inserted[i])
    }
}

这里的observer见上一篇博文。

第一次获取array类型数据的时候,也需要对数组中的对象进行监听

我们再回头看一下这个方法

class Observer {
    constructor(data) {
        if (Array.isArray(data)) {
            // 对编译方法进行处理
            data.__proto__ = arrayMethods
            // 对数组中的对象进行监听
            observerArray(data)
        } else {
            ...
        }
    }
}

写到这里,对数组进行劫持的原理就已经讲完了。

我们最后讨论一个问题,vue为什么对数组index的修改和lenght修改不做监听呢?

官网是这么解释的:

由于 JavaScript 的限制,Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue 当你修改数组的长度时,例如:vm.items.length = newLength

也就是说,当数组项非常大的时候,操作下标或length会有严重的性能问题。

「Tip」

** 为什么变异方法是七个?而不是其他方法?**

这是因为啊,只有这七个方法会改变数组嘛。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值