数组变异方法的实现原理
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可以订阅新属性。