「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」
数据响应式原理-数组
数组的响应式核心代码,在Observer
类的构造函数中
- 路径:Observer:src\core\observer\index.js
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 为数组中的每一个对象创建一个 observer 实例
this.observeArray(value)
}
复制代码
hasProto
这个属性是干嘛用的呢?
我们先来研究一下代码:
export const hasProto = '__proto__' in {}
复制代码
这么一看代码很简单了,hasProto
他的目的是为了判断当前浏览器是否支持__proto__
也就是用来处理兼容性的问题的
arrayMethods
路径:src\core\observer\array.js
const arrayProto = Array.prototype
// 使用数组的原型创建一个新的对象
export const arrayMethods = Object.create(arrayProto)
复制代码
这里就算是比较复杂了,首先我们要先回忆一下,我们对数组进行操作的方法
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
是不是就这几个!
然后vue
中对这几个方法进行了重写
methodsToPatch.forEach(function (method) {
// cache original method
// 保存数组原方法
const original = arrayProto[method]
// 调用 Object.defineProperty() 重新定义修改数组的方法
def(arrayMethods, method, function mutator (...args) {
// 执行数组的原始方法
const result = original.apply(this, args)
// 获取数组对象的 ob 对象
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
// splice的第三个方法就是新增的元素
case 'splice':
inserted = args.slice(2)
break
}
// 对插入的新元素,重新遍历数组元素设置为响应式数据
if (inserted) ob.observeArray(inserted)
// notify change
// 调用了修改数组的方法,调用数组的ob对象发送通知
ob.dep.notify()
return result
})
})
复制代码
protoAugment
function protoAugment (target, src: Object) {
target.__proto__ = src
}
复制代码
这个方法非常简单,他去重新设置当前数组的原生属性让他等于我们传递来的参数arrayMethods
,这个参数中有我们重写过的方法,但是他的原型指向数组的原型
copyAugment
和protoAugment
方法的前两个参数是一样的,但是多出arrayKeys
,这个属性是为了获取我们arrayMethods
中重写过的方法
function copyAugment (target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key])
}
}
复制代码
protoAugment 和 copyAugment的作用是一样的,就是把我们重写完毕的方法指向到数组的原型对象上
observeArray
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
复制代码
遍历数组中的对象,转换成响应式对象
总结
数组的数据响应式原理核心就是把原有的会改变数组数据的方法调用 Object.defineProperty() 重新定义修改数组的方法,当这些方法会被调用的时候,对插入的新元素,重新遍历数组元素设置为响应式数据