内容简介:在前面的几个首先我们知道数组下的一些方法是会对原数组照成影响的,有以下几个:这几个方法总的来说会照成几个影响:
在前面的几个 step
中,我们实现对象的属性的监听,但是有关于数组的行为我们一直没有处理。
我们先分析下导致数组有哪些行为:
arr.splice(1, 2, 'something1', 'someting2') arr[1] = 'something'
解决行为一
首先我们知道数组下的一些方法是会对原数组照成影响的,有以下几个:
- push
- pop
- shift
- unshift
- splice
- sort
- reverse
这几个方法总的来说会照成几个影响:
- 数组长度发生变化
- 数组内元素顺序发生变化
不像对象,如果对象的 key
值的顺序发生变化,是不会影响视图的变化,但数组的顺序如果发生变化,视图是要变化的。
也就是说当着几个方法触发的时候,我们需要视图的更新,也就是要触发 Dep
中的 notify
函数。
但是纵观我们现在实现的代码( step5
中的代码),我们并没有特地的为数组提供一个 Dep
。
并且上述的几个数组方法是数组对象提供的,我们要想办法去触发 Dep
下的 notify
函数。
我们先为数组提供一个 Dep
,完善后的 Observer
:
export class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
// 为数组设置一个特殊的 Dep
this.dep = new Dep()
this.observeArray(value)
} else {
this.walk(value)
}
Object.defineProperty(value, '__ob__', {
value: this,
enumerable: false,
writable: true,
configurable: true
})
}
/**
* 遍历对象下属性,使得属性变成可监听的结构
*/
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
/**
* 同上,遍历数组
*/
observeArray (items) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
复制代码
同样的在 defineReactive
我们需要处理数组添加依赖的逻辑
export function defineReactive(object, key, value) {
let dep = new Dep()
let childOb = observe(value)
Object.defineProperty(object, key, {
configurable: true,
enumerable: true,
get: function () {
if (Dep.target) {
dep.addSub(Dep.target)
Dep.target.addDep(dep)
// 处理数组的依赖
if(Array.isArray(value)){
childOb.dep.addSub(Dep.target)
Dep.target.addDep(childOb.dep)
}
}
return value
},
set: function (newValue) {
if (newValue !== value) {
value = newValue
dep.notify()
}
}
})
}
复制代码
ok 我们现在完成了依赖的添加,剩下的我们要实现依赖的触发。
处理方法:在数组对象调用特定方法时,首先找到的应该是我们自己写的方法,而这个方法中调用了原始方法,并触发依赖。
我们先来包装一下方法,得到一些同名方法:
const arrayProto = Array.prototype
// 复制方法
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* 改变数组的默认处理,将新添加的对象添加监听
*/
methodsToPatch.forEach(function (method) {
// 原始的数组处理方法
const original = arrayProto[method]
let mutator = function (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 新添加的对象需要添加监听
if (inserted) ob.observeArray(inserted)
// 触发 notify 方法
ob.dep.notify()
return result
}
Object.defineProperty(arrayMethods, method, {
value: mutator,
enumerable: false,
writable: true,
configurable: true
})
})
复制代码
ok 我们现在得到了一些列同名的方法,我只要确保在调用时,先调用到我们的方法即可。
有两种方式可以实现:
__proto__
具体到代码中的实现:
export class Observer {
constructor(value) {
this.value = value
if (Array.isArray(value)) {
this.dep = new Dep()
const augment = ('__proto__' in {})
? protoAugment
: copyAugment
// 覆盖数组中一些改变了原数组的方法,使得方法得以监听
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
...
}
...
}
/**
* 如果能使用 __proto__ 则将数组的处理方法进行替换
*/
function protoAugment (target, src, keys) {
target.__proto__ = src
}
/**
* 如果不能使用 __proto__ 则直接将该方法定义在当前对象下
*/
function copyAugment (target, src, keys) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
Object.defineProperty(target, key, {
value: src[key],
enumerable: false,
writable: true,
configurable: true
})
}
}
复制代码
测试一下:
let object = {
arrayTest: [1, 2, 3, 4, 5]
}
observe(object)
let watcher = new Watcher(object, function () {
return this.arrayTest.reduce((sum, num) => sum + num)
}, function (newValue, oldValue) {
console.log(`监听函数,数组内所有元素 = ${newValue}`)
})
object.arrayTest.push(10)
// 监听函数,数组内所有元素 = 25
复制代码
到现在为止,我们成功的在数组调用方法的时候,添加并触发了依赖。
解决行为二
首先先说明,数组下的索引是和对象下的键有同样的表现,也就是可以用 defineReactive
来处理索引值,但是数组是用来存放一系列的值,我们并不能一开始就确定数组的长度,并且极有可能刚开始数组长度为 0
,之后数组中的索引对应的内容也会不断的变化,所以为索引调用 defineReactive
是不切实际的。
但是类似于 arr[1] = 'something'
这样的赋值在数组中也是常见的操作,在 Vue
中实现了 $set
具体的细节这里不谈,这里实现了另一种方法,我们仅仅需要在数组对象下添加一个方法即可:
arrayMethods.$apply = function () {
this.__ob__.observeArray(this)
this.__ob__.dep.notify()
}
复制代码
测试一下:
object.arrayTest[1] = 10 object.arrayTest.$apply() // 监听函数,数组内所有元素 = 33 复制代码
到目前为了,一个完整的数据监听的模型也就完成了,我们可以使用 observe
方法来得到一个可监听结构,然后用 Watcher
添加依赖。
在设置值的时候就能成功触发依赖。
以上所述就是小编给大家介绍的《实现 VUE 中 MVVM - step6 - Array》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- php如何实现session,自己实现session,laravel如何实现session
- AOP如何实现及实现原理
- webpack 实现 HMR 及其实现原理
- Docker实现原理之 - OverlayFS实现原理
- 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?
- 自己实现集合框架(十):顺序栈的实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web容量规划的艺术
阿尔斯帕瓦 / 叶飞、罗江华 / 机械工业出版社 / 2010-1 / 29.00元
《Web容量规划的艺术》由John Allspaw(F订ickr的工程运营经理)撰写,结合了他个人在F1ickr成长过程中的许多经历和很多其他产业中同行的洞察力。在衡量增长、预测趋势、成本效益等方面,他们的经验都会给你一些可靠并有效的指导。 网站的成功是以使用和增长来衡量的,而且网站类公司的成败(生死)是依赖于他们是否有能力来衡量决定他们的基础结构,从而适应不断增长的需求。作者通过自身实践给......一起来看看 《Web容量规划的艺术》 这本书的介绍吧!
JS 压缩/解压工具
在线压缩/解压 JS 代码
随机密码生成器
多种字符组合密码