龙空技术网

从源码中学Vue(六)「解密」为什么操作数组的方法也会触发视图更新

畅哥聊技术 674

前言:

如今姐妹们对“vue重写数组的方法有哪些”都比较珍视,姐妹们都需要剖析一些“vue重写数组的方法有哪些”的相关内容。那么小编也在网络上网罗了一些对于“vue重写数组的方法有哪些””的相关文章,希望各位老铁们能喜欢,看官们一起来了解一下吧!

欢迎来到我的《从源码中学Vue》专题系列文章,更多精彩内容持续更新中,欢迎关注 :)

上一章节我大概分析了下在Vue中的 Watcher、Observer、Dep三者的关系,以及如何检测数据变化去更新视图的,以及通过源码分析了vue在初始化的时候是如何响应模板数据。

本章目标Object.defineProperty存在的问题为什么在我们操作数组的时候可以实现视图更新vue中数组劫持仍然存在的问题Object.defineProperty存在的问题

我们都知道,在Vue2.x中,内部实现的数据响应是通过Object.defineProperty来实现的,其实这个API是存在一些问题的。

比如,它不能兼听到数组的变化,什么意思呢?我来举个例子。

我通过Object.defineProperty定义了一个简单的方法,当我们的key为数组的时候,我们直接通过obj.list.push修改数组的时候,可以看到,我们的set方法并没有被触发。

我们再来通过vue来看下我们修改list的时候会发生什么?

可以看到,在vue中,我们对data下的list添加元素的时候,视图被更新了,也就是说,vue中操作数组的时候会发生数据响应。

结论:原生的Object.defineProperty不能劫持数组push等操作,但是vue却可以?

为什么在我们操作数组的时候可以实现视图更新

那么说明Vue内部一定做了一些兼容处理!

带着这个问题,跟我去源码中找找答案吧!!

那么在数据劫持的目录下很容易找到一个array.js的文件,我们打开它看看

文件目录:node_modules\vue\src\core\observer\array.js

它长这样!

先不用细看,看到

这一坨代码,我们就知道,vue针对这些方法做了处理。

那好,我们接下来具体分析下vue具体做了什么?

我们将原生的Array上的原型复制出来存储到了arrayMethods变量中。之所以要复制一个原型对象出来,是不能响应原生的数组的特性。

这有一个def方法,我们去找下它的定义。

目录:node_modules\vue\src\core\util\lang.js

它就是给一个Object对象添加Object.defineProperty,然后主要看第二个参数。value,通过调用我们知道,我们传入的实参是一个function 。

什么意思呢,这里我们就能够兼听到当数组的原生方法被触发后的回调。我再举个例子

这个示例中,我兼听了原生数组的push方法,当有push操作的时候,可以触发一个回调函数。

那么到这里,相信vue的数组响应原理应该就明白了吧。

到这里还没完,我们接着往下看

我圈出来的是为了实现数组原生的方法的执行。这里面的this就是指向的我们的要兼听的数据对象

在这个方法的最后,源码调用了ob.dep.notify()方法,那么这个ob是怎么来的呢?

 const ob = this.__ob__

这个对象我们可以在Observer类的中找到。

我们给value定义了一个__ob__的属性,然后赋值为this,这里面的this指向的是Observer对象。所以我们在最后可以调用ob.dep.notify()方法。

最后再来看这一坨代码

定义了一个inserted变量,它表示的是是否为添加。我们知道,给数组添加元素的方法有三个,push,unshift,splice。然后调用Observer兼听数组的observeArray方法。

splice这个方法当有第三参数的时候,可以视了给数组添加项。

思考一下,为什么要兼听数组的添加的方法呢?

其实前面也有提到过。比如,我们添加数组的项可能为一个对象,或者数组,如果这是些那么我们的Observer将要继续兼听这个对象,给这个对象添加get和set方法。

举个例子

当我们给数组添加成一个对象的时候,vue这时候也会劫持对象,我们可以看到对象的a已经被设置了get和set。

vue中数组劫持仍然存在的问题

在vue中,有一种情况下数组是不能被劫持的,那就是当我们手动通过索引改变数组的某一项的时候,不会触发视图的更新。比如这样:

视图并没有变化

这个解决办法就是重新给list赋值 ,我们知道数组的concat和slice方法都可以返回一个全新的数组。我们可以这样做。

vm.list = vm.list.concat([]);  或者vm.list = vm.list.slice(); 

到这里,Vue劫持数组的原理基本上就分享完了。

总结:原生的 Object.defineProperty并不能劫持数组的变化。vue是通过重写了数组的Array原型上的方法,实现了数据的劫持当我们通过索引去修改数组的时候,可以调用数组的slice和concat方法来手动实现视图更新Object.defineProperty可以实现JS内部函数的劫持。

这里是畅哥聊技术 《从源码中学Vue》系列文章,更多精彩内容持续更新中,敬请期待。

未完待续。。。

标签: #vue重写数组的方法有哪些