首先将数据响应的关系图通过图形的形式画出来
通过流程图可以看到 大致分为几个不同的方法和类
- 能够对数据进行观察的 observe 和 observer类 对数组的处理
- 对数据进行观察的 defineReactive
- 数据依赖的 Dep
- 对依赖添加处理的 Watch
几个文件的关系图
- index.js
import defineReactive from './data/defineReactive.js'
import {observe} from './data/observer'
import Watcher from './data/watcher.js'
var obj = {
'a': {
"b":{
'n': 5
}
},
'b': 6,
'g':['90','76','766','56']
}
observe(obj)
new Watcher (obj, 'a.b.n', (val) => {
console.log('********',val)
})
obj.a.b.n = 890
- observer.js
import {def} from './utils.js'
import defineReactive from './defineReactive.js'
import {arrayMethods} from './array.js'
import Dep from './dep.js'
export default class Observer {
constructor (value) {
this.dep = new Dep()
// 一个不可枚举属性 this 在类中不是指的类本身 , 指的是实例
// 通过def 函数给 value 添加__ob__属性,并且指向 实例本身
def(value, '__ob__', this, false)
// Observer 的终极目标是将 obj 中每个层级的属性转化为响应式的
if (Array.isArray(value)) {
// 如果是数组,强行将原型指向 新的地址
Object.setPrototypeOf(value, arrayMethods)
// 让这个数组变的 observe
this.observeArray(value)
}else{
this.walk(value)
}
}
walk (value) {
// 遍历每个 KEY 值去将每个层级的属性变为响应式
for(let key in value) {
defineReactive(value,key)
}
}
observeArray (arr) {
for (let i=0, l= arr.length; i<l; i++) {
observe(arr[i])
}
}
}
export function observe (value) {
// 判断是不是对象,如果不是对象直接返回
if (typeof value !== 'object') return;
var ob;
// 判断该对象上是不是绑定了__ob__ 如果绑定了 直接使用
if (typeof value.__ob__!=='undefined') {
ob = value.__ob__
}else {
ob = new Observer(value)
}
return ob
}
- utils.js
// def 函数利用defineProperty 给对象添加属性和属性值
export function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value : val,
enumerable, // 是不是课枚举的
writable: true, // 是不是可写的
configurable: true // 是不是课设置的
})
}
// 返回一个高阶函数, 在watcher 中使用,是获取对象的值使用的
export function parsePath (str) {
let segments = str.split('.')
return (obj) => {
for (let i =0; i< segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]]
}
return obj
}
}
- array.js
// 这里是对数组中的一些函数,利用装饰者模式来添加一些新的功能--触发 数据更新
import {def} from './utils.js'
// 找到Array 的原型对象
const arrayPrototype = Array.prototype
// 以array.prototype 为原型创建一个 arrayMethods, 这样arrayMethods 上就有了 Array 上的方法
export let arrayMethods = Object.create(arrayPrototype)
let methodNeedChange = [
'push','pop','shift','unshift','splice','sort','reverse'
]
methodNeedChange.forEach((methodName) => {
// 备份原来的方法
const original = arrayMethods[methodName]
// 对需要改变的函数重新定义
def(arrayMethods,methodName,function() {
// 首先执行老方法,利用 apply, 来执行,
const result = original.apply(this, arguments)
// 在每个值的本生绑定了__ob__属性, 绑定的实例本生
let ob = this.__ob__;
let args = [...arguments] // 将类数组变为数组
// 有三种方法 会往数组中添加内容, 需要将添加的内容页变为 observe
// push unshift splice
let inserted = []
switch (methodName) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
// splice(开始的下标,数量,要添加的值)
inserted = args.slice(2);
break;
}
if (inserted) {
// 让新项也变为 observe
ob.observeArray(inserted)
}
console.log('啦啦啦')
ob.dep.notify()
return result
},false)
})
- defineReactive.js
import {observe} from './observer'
import Dep from './dep.js'
export default function defineReactive (data,key,val) {
const dep = new Dep()
if (arguments.length == 2) {
val = data[key]
}
let childOb = observe(val)
Object.defineProperty(data,key,{
enumerable: true, // 可被枚举
configurable: true, // 可以被设置,比如删除
get () {
// 如果处于一个依赖收集阶段
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
}
console.log('你试图访问obj 的'+key+'属性' + val)
return val
},
set (newVal) {
console.log('你获取了'+key+'值' + newVal)
if (val===newVal) {
return
}
val = newVal
// 当设置了新值, 需要继续添加观察
childOb = observe(val)
dep.notify()
}
})
}
- dep.js
let uid =0;
export default class Dep {
constructor() {
console.log('我是DEP 类的构造器')
this.id = uid++
// 发布订阅者模式 创建一个 订阅者数组 放的是 watcher 实例
this.subs = []
}
addSub (sub) {
this.subs.push(sub)
}
// 添加依赖
depend () {
// Dep.target是指定的一个全局位置
if (Dep.target) {
this.addSub(Dep.target)
}
}
notify () {
console.log('我是 notify')
// 浅克隆一份
let subs = this.subs.slice()
// 遍历
for (let i=0,l=subs.length; i< l; i++) {
subs[i].update()
}
}
}
- watcher.js
import Dep from './dep.js';
import {parsePath} from './utils.js'
let uid = 0
export default class Watcher{
constructor(target, expression, callBack) {
this.id = uid++
this.target = target;
// parsePath 就是解析表达式的 一个高阶函数
this.getter = parsePath(expression)
this.callBack = callBack;
this.value = this.get()
console.log('我是watcher的构造器')
}
update () {
this.run()
}
get () {
// 进入依赖收集阶段 让全局的Dep.target 设置为 watcher 本身
Dep.target = this;
const obj = this.target;
// 只要能找 就一直找
var value
try {
value = this.getter(obj)
} finally {
Dep.target = null
}
return value
}
// 得到 并且唤起
run () {
this.getAndInvoke(this.callBack)
}
getAndInvoke (cb) {
const value = this.get();
if (value !== this.value || typeof value == 'object') {
const oldVal = this.value
this.value = value
// 这里就是 watcher 中的 参数, 新值 和旧值
cb.call(this.target,value,oldVal)
}
}
}
最后总结下:
- 在 observe 中通过遍历的方式,给对象和数组添加 添加一个__ob__ 属性,属性值是Observer实例本身。每个实例上也绑定一个 dep
- 通过对象遍历的方式,给每个值都变为响应式的
- 在defineReactive 中给对象和对象的属性添加为响应式, 对值再调用 observe 是每个层都变为响应式
- 调用 new Watcher 的时候 , 首先会调用 watcher中的get 方法,将对象本身设置为Dep.target 的静态属性值,然后获取当前的对象,对应的属性值。 这样就触发了defineProperty 中的get , 来收集依赖, 实际上就是将 watcher 实例本身收集了。
- 在index.js 中再改变对象中某个值的时候,会触发 defineProperty 中的set ,触发 notify ,进而将dep 中观察这个对象时收集的依赖触发。就是出触发 watcher 中的update .update中 会重新获取该对象的该属性值,会得到newVal, 也会再次收集依赖, 最后调用 回调函数,将oldVal和newOld 作为参数。