数组变异方法的实现原理
path: vue/src/core/observer/array.js
1 | import { def } from '../util/index' |
1 | /** |
整段代码执行下来,创建了一个对象arrayMethods,它的原型只想数组原型,并且它有这么成员方法:’push’,
‘pop’, ‘shift’, ‘unshift’, ‘splice’, ‘sort’, ‘reverse’!
很明显,这样还不够!这样还能直接通过vue数组实例的点操作符调用变异方法!还需要将这些挂在vue实例数组的原型链上!
1 | export class Observer { |
在坚持数组时,会将arrayMethods,根据实际情况挂在到当前这个数组的原型链上!在可以设置原型执行时,直接改变当前数组原型指向,改为arrayMethods;否则,直接将arrayMethods的方法,复制到当前数组上,作为当前数组的成员方法!
回看上面的代码(这一处:def(arrayMethods, method, function mutator (...args)
),在这一块代码可以发现this.__ob__
!经过以上分析,我们知道arrayMethods最后会挂在到vue数组上,那么这个this指向的就是这个数组,那么__ob__
应该就是在vue数组原型链上或数组ownProperties上的!
似曾相似,在哪里遇过~
1 | export class Observer { |
在创建Observer实例时,会将当前实例挂在到当前观察数据的__ob__
上,对vue数组而言,这个被观察的数据就是它了!
既然__ob__
是Observer实例,当然调用劫持数组的方法ob.observeArray(inserted)
!这里插一句题外话,虽然新增元素是属于当前数组的,但还是在被劫持这个行为上,他们是相互独立的,所以这里就是不使用ob.ob.observeArray去劫持,而使用inserted.forEach((item) => observe(it))
劫持也是可以的~
这里需要当前数组的__ob__
,主要是为了通知到这个数组的订阅者!
总结
- 变异方法通过对数组原型方法的拦截对原有方法进行处理,拦截的方式分两种:1. 可以设置原型的情况下,通过改变vue数组的原型指向进行拦截;2. 1不可行的情况下,则在vue数组的ownProperties上创建同名成员方法拦截!
- 编译数组方法可以通知watche,是因为最后调用
ob.dep.notify()
,通知了订阅者,这就是本质区别; ob.observeArray(inserted)
再次坚持新元素是为了让别的watcher可以订阅新属性。