JavaScript中Array.prototype上有很多常用的方法,MDN上也给出了介绍,虽然方法众多,但是可以改变数组内容的只有7个(fill和copyWithin处于试验阶段,所以源码中没有适配),分别是:pop、push、shift、unshift、splice、sort、reverse。
这里的主要内容放在代码注释中,能够看得更清楚
Vue处理数组响应式的办法是劫持数组原型方法,将数组元素的原型替换为重写的数组原型方法
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
// 列举出所有可以改变数组内容的方法名
const methods = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methods.forEach(function (method) {
// 原来数组的方法
const original = arrayProto[method]
// def工具,为对象设置函数属性
def(arrayMethods, method, function mutator (...args) {
// 执行原方法,指定this
const result = original.apply(this, args)
// 每个属性都维护一个子的观察者,后面会说到
const ob = this.__ob__
let inserted
// 处理新加入的参数
switch (method) {
case 'push':
case 'unshift':
// push和unshift都会在这里处理,获取参数,参数也需要做响应式处理
inserted = args
break
case 'splice':
// splice的新加的参数是从第三位开始的,如arr.splice(2, 1, 'bar')
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 通知更新,后面依赖收集会说到
ob.dep.notify()
// 将原方法执行的结果返回
return result
})
})
// util.js
export function def(obj, key, value, enumerable = false) {
Object.defineProperty(obj, key, {
value,
enumerable,
writable: true,
configurable: true
})
}
上面这段代码有点长,来总结一下它的主要功能
-
开始先使用
Object.create创建一个新的对象,原型是Array.prototype, -
然后列举能够改变数组的7个方法,遍历列举出来的数组方法,给创建出来的对象添加函数属性,期间执行原方法获取返回值,如果方法是可以传参的那就要对参数再进行观测,
-
然后通知视图更新,最后返回函数的返回值
现在具有响应式功能的数组方法已经重写完了,下一步就是将重写过的数组方法挂载到数组上了,这时候要将之前的Observer稍加改造,对要坚挺的数据进行类型检测,如果是数组元素就将重写的数组方法挂载到原型上
export default class Observer {
constructor(value) {
this.value = value
// 用于维护依赖,后面会说
this.dep = new Dep()
def(value, '__ob__', this)
// 类型检测
if (Array.isArray(value)) {
// 使用setPrototypeOf可以将第二个参数作为第一个参数__proto__
Object.setPrototypeOf(value, arrayMethods)
this.observeArray(value)
} else {
// 之前有observe的过滤,能够到这里的不是数组就是对象
this.walk(value)
}
}
walk(obj) {
Object.keys(obj).forEach((key) => defineReactive(obj, key, obj[key]))
}
// 将数组元素遍历观察
observeArray(arr) {
arr.forEach((i) => observe(i))
}
}
到这里数据观察过程已经基本了解了,但是这里并不是最终版,这里可以在数据发生变化时通过setter来更新视图,但是如果数据并没有被页面引用呢,即便是引用了,任何一个数据变化都会导致页面更新,代价太大了,这就需要我们后面要说的依赖收集了。
本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情