内容简介:在
在 step4
中,我们大致实现了一个 MVVM
的框架,由3个部分组成:
defineReactive Dep Watcher
defineReactive
和 Dep
改造了对象下的某个属性,将目标变成了观察者模式中的目标,当目标发生变化时,会调用观察者;
Watcher
就是一个具体的观察者,会注册到目标中。
之前的代码实现了观察者模式,使得数据的变化得以响应,但是还是有两个需要优化的地方:
defineReactive
解决
问题2
先解决第二个问题,我们仅仅需要把代码进行划分即可,然后用 webpack/babel
打包即可,当然这里就不说如何去配置 webpack
,使用 webpack/babel
我们就可以使用 ES6
的语法和模块系统了。
但是为了偷懒,我把代码直接放在 node
环境中执行了,但是 import
语法需要特定的 node
版本,我这里使用的是 8.11.1
(版本网上都应该是支持的),同时需要特定的文件后缀(.mjs)和命令 node --experimental-modules xxx.mjs
。
执行方式进入到 step5
的目录下,命令行运行 node --experimental-modules test.mjs
即可。
当然你也可以用 webpack/babel
进行打包和转码,然后放到浏览器上运行即可。
问题1
对于问题1,我们需要做的仅仅是实现一个方法进行遍历对象属性即可。我们把这个过程抽象成一个对象 Observe
。至于为什么要把这个过程抽象成一个对象,后面会说。
注:由于是在 node
环境下运行代码,这里就直接用 ES6
的语法了。同样的我把别的模块也用 ES6
语法写了一遍。
export class Observer {
constructor(value) {
this.value = value
this.walk(value)
// 标志这个对象已经被遍历过,同时保存 Observer
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]])
}
}
}
复制代码
从代码可以看出,这个类在实例化的时候自动遍历了传入参数下的所有属性( value
),并把每个属性都应用了 defineReactive
。
为了确保传入的值为对象,我们再写一个方法来判断。
export function observe (value) {
// 确保 observe 为一个对象
if (typeof value !== 'object') {
return
}
let ob
// 如果对象下有 Observer 则不需要再次生成 Observer
if (value.hasOwnProperty('__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (Object.isExtensible(value)) {
ob = new Observer(value)
}
return ob
}
复制代码
函数返回该对象的 Observer
实例,这里判断了如果该对象下已经有 Observer
实例,则直接返回,不再去生产 Observer
实例。这就确保了一个对象下的 Observer
实例仅被实例化一次。
上面代码实现了对某个对象下所有属性的转化,但是如果对象下的某个属性是对象呢?
所以我们还需改造一下 defineReactive
具体代码为:
export function defineReactive(object, key, value) {
let dep = new Dep()
// 遍历 value 下的属性,由于在 observe 中已经判断是否为对象,这里就不判断了
observe(value)
Object.defineProperty(object, key, {
configurable: true,
enumerable: true,
get: function () {
if (Dep.target) {
dep.addSub(Dep.target)
Dep.target.addDep(dep)
}
return value
},
set: function (newValue) {
if (newValue !== value) {
value = newValue
dep.notify()
}
}
})
}
复制代码
ok 我们来测试下
import Watcher from './Watcher'
import {observe} from "./Observe"
let object = {
num1: 1,
num2: 1,
objectTest: {
num3: 1
}
}
observe(object)
let watcher = new Watcher(object, function () {
return this.num1 + this.num2 + this.objectTest.num3
}, function (newValue, oldValue) {
console.log(`监听函数,${object.num1} + ${object.num2} + ${object.objectTest.num3} = ${newValue}`)
})
object.num1 = 2
// 监听函数,2 + 1 + 1 = 4
object.objectTest.num3 = 2
// 监听函数,2 + 1 + 2 = 5
复制代码
当然为了更好的了解这个过程,最好把 step5
目录中的代码拉下来一起看。至于之前实现的功能这里就不专门写测试了。
最后
最后解释下为什么要把 遍历对象属性这个过程抽象成一个对象
-
对象在
js下存放是是引用,也就是说有可能几个对象下的某个属性是同一个对象下的引用,如下let obj1 = {num1: 1} let obj2 = {obj: obj1} let obj3 = {obj: obj1} 复制代码如果我们抽象成对象,而仅仅是函数调用的话,那么
obj1这个对象就会遍历两次,而抽象成一个对象的话,我们可以把这个对象保存在obj1下( ob 属性),遍历的时候判断一下就好。 -
当然解决上面问题我们也可以在
obj1下设置一个标志位即可,但是这个对象在之后会有特殊的用途,先这样写吧。(与数组和Vue.set有关)
在代码中我为 Dep
和 Watch
添加了 id
这个暂时用不到,先加上。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- php如何实现session,自己实现session,laravel如何实现session
- AOP如何实现及实现原理
- webpack 实现 HMR 及其实现原理
- Docker实现原理之 - OverlayFS实现原理
- 为什么实现 .NET 的 ICollection 集合时需要实现 SyncRoot 属性?如何正确实现这个属性?
- 自己实现集合框架(十):顺序栈的实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Design Handbook
Baeck, Philippe de 编 / 2009-12 / $ 22.54
This non-technical book brings together contemporary web design's latest and most original creative examples in the areas of services, media, blogs, contacts, links and jobs. It also traces the latest......一起来看看 《Web Design Handbook》 这本书的介绍吧!