1、创建观察者Observer类
Observer类主要是深度监听所有key值,(准备)收集所有依赖和等待被触发通知。
准备这两个字,是等待get/set触发。
什么个意思?就是说Observer类做深度的欲准备。
class Observer{constructor(value){this.value = valueif (Array.isArray(value)) {// 这里不做介绍 代码有点多} else {// Object的深度监听this.walk(value)}}walk(obj){const keys = Object.keys(obj)for(let i=0;i<keys.length;i++){defineReactive(obj,keys[i])}}}function defineReactive(obj,key,value){// 创建依赖Dep类const dep = new Dep()if(arguments.length===2){value = obj[key]}if (typeof value === 'object') {// 递归将子重新深度监听new Observer(value)}Object.defineProperty(obj, key, {configurable: true,enumerable: true,get() {// 在get中收集依赖dep.depend()return value},set(newVal) {if (value === newVal) {return}value = newVal// 在set中通知依赖dep.notify()}})}
2、创建Dep类
Dep依赖收集类,主要类中有:添加依赖,删除依赖,通知依赖。
class Dep{constructor(){this.subs = []}addSub(sub){this.subs.push(sub)}removeSub(sub){remove(this.subs, sub)}depend() {if (window.target) {this.addSub(window.target)}}notify() {const subs = this.subs.slice()for (let i = 0; i < subs.length; i++) {subs[i].update()}}}function remove(arr, item) {if (arr.length > 0) {if (~arr.indexOf(item)) {return arr.splice(arr.indexOf(item), 1)}}}
3、Watcher监听类
watcher就是被Dep类收集起来的依赖
watcher做什么事情呢?
第一步:Watcher实例化,执行构造函数。
第二步:调用this.get方法,通过window.target = this把实例自身赋给了全局的一个唯一对象window.target上,然后通过let value = this.getter.call(vm, vm)获取被依赖的数据,获取被依赖数据的目的是触发该数据上面的getter,在getter里会调用dep.depend()收集依赖,而在dep.depend()中取到挂载window.target上的值并将其存入依赖数组中,在get()方法最后将window.target释放掉
第三步:当数据发生改变时,在Observer中setter中调用了dep.notify()方法,在dep.notify()方法中,遍历所有依赖(即watcher实例),执行依赖的update()方法,也就是Watcher类update实例方法
class Watcher{constructor(vm,expOrFn,cb){this.vm = vmthis.cb = cbthis.getter = parsePath(expOrFn)this.value = this.get()}get () {window.target = this;const vm = this.vmlet value = this.getter.call(vm, vm)window.target = undefined;return value}update () {const oldValue = this.valuethis.value = this.get()this.cb.call(this.vm, this.value, oldValue)}}const reg =/[^\w.$]/function parsePath(path) {if (reg.test(path)) {return}const segments = path.split('.') // userInfo.age => [userInfo,age]return function (obj) { // obj ={userInfo:{age:20}}for (let i = 0; i < segments.length; i++) {obj = obj[segments[i]]}return obj // 20}}
