const data = {  flag: false,  firstName: 'Zheng',  lastName: 'Yimeng'}const computed = {  name() {    if (!data.flag) {      return '你拿不到'    }    return data.firstName + ' ' + data.lastName  },}function observe(obj) {  const keys = Object.keys(obj)  /** 须要留神这里,我并没有对传入的 obj 自身做响应式解决,是为了简化代码 (vue 源代码对传入的对象也做了解决) */  keys.forEach(key => {    observer(obj, key)  })}function observer(obj, key) {  let value = obj[key] //?1 为什么须要这个货色  let dep = new Dep() // 每个属性都有一个本身的 dep。  Object.defineProperty(obj, key, {    get() {      if (Dep.target) {        console.log(`收集依赖 ${key}`)        Dep.target.addDep(dep)      }      return value //?1 因为在这里写 obj[key] 的时候相当于从新拜访这个 key,会再次触发 get 办法,进入死循环    },    set(newVal) {      if (value === newVal) return      value = newVal      if (dep.subs.length) {        console.log(`${key} 扭转了,我要更新它记录的 watcher 了`)        dep.notify()      }    }  })}function initComputed(computed) {  /** 留神我这里搞 computed 从新拆来搞只是为了让大家看明确,vue 源代码和我不一样,然而原理相似 */  const watchers = {}  const keys = Object.keys(computed)  keys.forEach(key => {    watchers[key] = new Watcher(computed[key])  })  return watchers}function Dep () {  this.subs = []}Dep.prototype.notify = function() {  this.subs.forEach(watcher => watcher.update())}function Watcher(func) {  this.get = func  this.deps = []  Dep.target = this  this.value = this.get() // 函数体是 { return data.firstName + ' ' + data.lastName },所以会调用 data.firstName 和 data.lastName 的 get 办法  Dep.target = null}Watcher.prototype.addDep = function(dep) {  // 这里为什么须要记录 dep? 因为:假如当初 flag 为 true,那么咱们的 computed.name 收集依赖的时候收集到了 data.flag, data.firstName, data.lastName,这三个属性每个都有一个 dep,批改其中一个都会从新调用 dep.notify() 从而更新 name 值, 然而当咱们批改其中的一个属性的时候,这个收集的依赖其实就曾经扭转了,比方批改了 flag = false,再批改 data.firstName 和 data.lastName 的时候,就不应该再从新求 computed.name 值了,因为走不到那一步,那么 data.firstName 和 data.lastName 的 dep 就应该为空,这样在批改 data.firstName 和 data.lastName 的时候因为他们的 dep 为空就不会从新求 computed.name 值了  this.deps.push(dep)  dep.subs.push(this) // 到这里的时候,还记得吗?本身属性的 watcher 就被记录下来了}Watcher.prototype.update = function() {  this.deps.forEach(dep => dep.subs = []) // 在这里把该 计算属性 对应的每个 依赖属性 的 dep 都清空  Dep.target = this  let value = this.get() // 这里调用 get 后从新收集依赖  Dep.target = null  if (this.value !== value) {    this.value = value    // value扭转,渲染页面  }}function MyVue({data, computed}) {  observe(data)  this.watchers = initComputed(computed)  // 这里把属性简略地绑给实例自身  Object.keys(this.watchers).forEach(key => { // 这里 computed 我假如不让手动批改它,所以不作解决    Object.defineProperty(this, key, {      get() {        return this.watchers[key].value      }    })  })  Object.keys(data).forEach(key => {    Object.defineProperty(this, key, {      get() {        return data[key]      },      set(newVal) {        data[key] = newVal      }    })  })}window.vm = new MyVue({data, computed})

初始化data

observe(data)
function observe(obj) {  const keys = Object.keys(obj)  /** 须要留神这里,我并没有对传入的 obj 自身做响应式解决,是为了简化代码 (vue 源代码对传入的对象也做了解决) */  keys.forEach(key => {    observer(obj, key)  })}function observer(obj, key) {  let value = obj[key] //存储旧值  let dep = new Dep() // 每个属性都有一个本身的 dep。  Object.defineProperty(obj, key, {    get() {      if (Dep.target) {        console.log(`收集依赖 ${key}`)        Dep.target.addDep(dep)      }      return value //?1 因为在这里写 obj[key] 的时候相当于从新拜访这个 key,会再次触发 get 办法,进入死循环    },    set(newVal) {      if (value === newVal) return      value = newVal      if (dep.subs.length) {        console.log(`${key} 扭转了,我要更新它记录的 watcher 了`)        dep.notify()      }    }  })}

为每个data属性生成一个dep对象,在属性的get办法中,触发watch.addDep办法实现dep和watch的互相收集。在set办法中告诉watch

Dep对象

subs:存储watch
notify:属性更新了揭示相干watch

function Dep () {  this.subs = []}Dep.prototype.notify = function() {  this.subs.forEach(watcher => watcher.update())}

初始化computed

this.watchers = initComputed(computed)
function initComputed(computed) {  /** 留神我这里搞 computed 从新拆来搞只是为了让大家看明确,vue 源代码和我不一样,然而原理相似 */  const watchers = {}  const keys = Object.keys(computed)  keys.forEach(key => {    watchers[key] = new Watcher(computed[key])  })  return watchers}

为每个computed生成一个watch。

watch

function Watcher(func) {  this.get = func  this.deps = []  Dep.target = this  this.value = this.get() // 函数体是 { return data.firstName + ' ' + data.lastName },所以会调用 data.firstName 和 data.lastName 的 get 办法  Dep.target = null}Watcher.prototype.addDep = function(dep) {  // 这里为什么须要记录 dep? 因为:假如当初 flag 为 true,那么咱们的 computed.name 收集依赖的时候收集到了 data.flag, data.firstName, data.lastName,这三个属性每个都有一个 dep,批改其中一个都会从新调用 dep.notify() 从而更新 name 值, 然而当咱们批改其中的一个属性的时候,这个收集的依赖其实就曾经扭转了,比方批改了 flag = false,再批改 data.firstName 和 data.lastName 的时候,就不应该再从新求 computed.name 值了,因为走不到那一步,那么 data.firstName 和 data.lastName 的 dep 就应该为空,这样在批改 data.firstName 和 data.lastName 的时候因为他们的 dep 为空就不会从新求 computed.name 值了  this.deps.push(dep)  dep.subs.push(this) // 到这里的时候,还记得吗?本身属性的 watcher 就被记录下来了}Watcher.prototype.update = function() {  this.deps.forEach(dep => dep.subs = []) // 在这里把该 计算属性 对应的每个 依赖属性 的 dep 都清空  Dep.target = this  let value = this.get() // 这里调用 get 后从新收集依赖  Dep.target = null  if (this.value !== value) {    this.value = value    // value扭转,渲染页面  }}

deps:存储相干依赖dep
1、构造函数中调用了this.get办法就会触发相干属性的get办法。进而触发watch.addDep办法,dep和watch互相收集
2、依赖更新了,watch.update触发。清空上次的依赖数组;从新计算computed值;从新构建依赖数组