在Vue.js中,当我们使用数据对象的时候,如果改变了该对象中的某个属性值,那么Vue就会自动更新相应的DOM。但是,如果该对象是一个数组,我们修改数组中的元素时,Vue却不能够自动更新DOM,这时候就需要手动调用$set或者使用Vue.set进行操作。
Vue是如何监听数组变化的呢?接下来通过源码解析来详细说明一下。
源码解析
1. 监听数据
Vue中对于数组的监听是通过重写数组原型方法来实现的,即在数组上添加一些额外的方法来拦截对数组数据的修改,然后再通知视图进行更新。
----- ---------- - --------------- ----- ------------ - ------------------------- -- --------- ----- -------------- - - ------- ------ -------- ---------- --------- ------- --------- - ----------------------------- -- - ----- -------- - ------------------ ----------------- ------- -------- ------- --------- - ----- ------ - -------------------- ----- ----- -- - ----------- --- -------- ------ -------- - ---- ------- ---- ---------- -------- - ---- ----- ---- --------- -------- - ------------- ----- - -- ---------- ------------------------- --------------- ------ ------ -- -- ----- -------- - ----------- ------- - ---------- - ----- -------- - --- ----- ------------ - - ---------- --------- ----- -- ---------------------- - -- ---- -- ---------- - ------------------- ------------- - ---- - ------------------ ------------- --------------- - ------------------------ - ---- - -- ---- ---------------- - - ------------ ------- - --- ---- - - -- - - ------------- - - -- ---- - ----------------- - - -
数组的监听代码位于Observer类中,当用户创建一个新的Vue实例时,Vue会对该实例数据对象进行递归遍历,将每个属性都转化为getter/setter,并且在需要的情况下添加上一些特殊的观察处理的逻辑,即Observer类中的observe方法。
如果某个数据对象被观测过了,则会给该对象添加一个__ob__属性。我们可以看到,在Observer类的构造函数中,如果传入的值是一个数组,那么就会调用protoAugment(value, arrayMethods)
或者copyAugment(value, arrayMethods, methodsToPatch)
这两个函数分别给数组添加原型方法。
其中,protoAugment()
函数使用原型链的方式将目标对象的原型指向一个拥有被增强方法的原型对象,这样目标对象就可以通过原型链访问到增强的方法了。
而copyAugment()
函数则是遍历需要拦截的数组方法,逐个添加到目标对象上。如果浏览器不支持原型对象的__proto__
属性,那么就采用后者的方式。
2. 拦截数组变化
在上面的代码中,我们可以看到对于拦截数组变化的方法都是通过重写数组原型来实现的。这里以push方法为例进行说明:
----- -------- - ------------------ ----------------- ------- -------- ------- --------- - ----- ------ - -------------------- ----- ----- -- - ----------- --- -------- ------ -------- - ---- ------- ---- ---------- -------- - ---- ----- ---- --------- -------- - ------------- ----- - -- ---------- ------------------------- ------------- ---------------------------------------------------------- ---------- -------------------------------------------------------------------------------------