资讯专栏INFORMATION COLUMN

简单实现vue中的依赖收集与响应

SexySix / 479人阅读

摘要:但中的视图渲染是实时的,视图层依赖于数据层,数据变化了,视图层也会跟着变化,不需要手动更新。被读取被修改运行所有运行看看我们加多一个试试这就是中的一个依赖对应多个

开始

声明一个对象man,可以视为vue中的data

let man = {
  height: 180,
  weight: 70,
  wealth: 100000000
}
添加Observer

作用在于将参数对象的属性变为响应式,只要对象的属性被读取或者被修改都能观察到。然后新建一个Observer实例,将man作为参数扔进去。这里的proxyData是将man的属性代理到以man为参数的Observer实例上去。

class Observer {
  constructor(obj) {
    this.walk(obj)
  }
  walk(obj) {
    Object.keys(obj).forEach(prop => {
      this[prop] = obj[prop]
      this.proxyData(obj, prop)
      this.defineReactive(this, prop, obj[prop])      
    })
  }
  proxyData(obj, prop) {
    let _this = this
    Object.defineProperty(obj, prop, {
      get() {
        return _this[prop]
      },
      set(newVal) {
        _this[prop] = newVal
      }
    })
  }
  defineReactive(obj, prop, val) {
    Object.defineProperty(obj, prop, {
      get() {
        console.log(`${prop} - 被读取!`)
        return val
      },
      set(newVal) {
        if (newVal == val) return
        val = newVal
        console.log(`${prop} - 被修改!`)
      }
    })
  }
}

new Observer(man)

这时打印一下man


现在man的属性都是由Observer实例所对应的属性的getter来返回,只有在查看时会被触发


对man的属性进行修改也会触发实例对应属性的setter

添加Watcher

现在的Watcher有点像vue中的computed,实际上就是定义一个计算属性,这个计算属性依赖于前面man中的某些属性,由他们计算而得。

class Watcher {
  constructor(obj, prop, computed) {
    this.getVal(obj, prop, computed)
  }

  getVal(obj, prop, computed) {
    Object.defineProperty(obj, prop, {
      get() {
        console.log(`computed属性 - ${prop}被读取!`)
        return computed()
      },
      set() {
        console.error("计算属性不可被修改!")
      }
    })
  }
}

new Watcher(man, "strength", () => {
  let {height, weight} = man
  if (height > 160 && weight > 70) return "strong"
  return "weak"
})


看起来没什么问题,所依赖的属性如果变了,计算属性只要再被查看(get方法)一次就可以更新了。但vue中的视图渲染是实时的,视图层依赖于数据层,数据变化了,视图层也会跟着变化,不需要手动更新。类比到这个例子就是计算属性如何才能在其所依赖的属性发生变化时被通知从而触发应有的事件?

这时我们先给Watcher加多一个callback,用于处理当依赖的数据被修改时,我这个计算属性该怎么响应

比如当依赖被修改时,我们就把这个计算属性的值打印出来

class Watcher {
  constructor(obj, prop, computed, callback) {
    this.getVal(obj, prop, computed, callback)
  }

new Watcher(man, "strength", () => {
  let {height, weight} = man
  if (height > 160 && weight > 70) return "strong"
  return "weak"
}, () => {
  console.log(`i am so ${man.strength} !`)
})

一切都准备好了,接下来就是该如何实现?

我们先看下Watcher中getVal这个方法

getVal(obj, prop, computed, callback) {
  Object.defineProperty(obj, prop, {
    get() {
      console.log(`computed属性 - ${prop}被读取!`)
      return computed()
    },
    set() {
      console.error("计算属性不可被修改!")
    }
  })
}

当我们查看计算属性时,会调用computed这个方法,相当于查看了其所依赖的height和weight属性,而在上面我们已经让man的所有属性都拥有了get方法,即他们被查看时我们是不是可以把callback塞给他们?
这时候我们引进一个桥梁,来连接Watcher和Observer。

添加Dep

Dep的用处在于当某一个属性(以下称‘自己’)被依赖了,将依赖自己的粉丝(们)--也就是Watcher(s),收集起来,假如自己发生了变化,能够及时通知粉丝们。

class Dep {
  constructor() {
    this.deps = []
  }
  getDeps() {
    if (!Dep.target || this.deps.includes(Dep.target)) return
    console.log("依赖添加", Dep.target)
    this.deps.push(Dep.target)
  }
  notify() {
    this.deps.forEach(dep => {
      dep()
    })
  }
}

这里的Dep.target就是前面所说的callback方法了。这时我们改一下Watcher中的getVal

getVal(obj, prop, computed, callback) {
  Object.defineProperty(obj, prop, {
    get() {
      Dep.target = callback
      console.log(`computed属性 - ${prop}被读取!`)
      return computed()
    },
    set() {
      console.error("计算属性不可被修改!")
    }
  })
}

在计算属性被查看时,将callback赋值给Dep.target,接下来就会调用其所依赖属性的getter,我们只要在getter里把callback给收集起来就行了。接下来修改依赖属性的getter方法。

defineReactive(obj, prop, val) {
  let dep = new Dep()
  Object.defineProperty(obj, prop, {
    get() {
      console.log(`${prop} - 被读取!`)
      dep.getDeps()  // 依赖收集
      return val
    },
    set(newVal) {
      if (newVal == val) return
      val = newVal
      console.log(`${prop} - 被修改!`)       
    }
  })
}

这时watcher的callback都被依赖属性给收集起来了,当依赖属性发生变化时只要去运行这些callback就可以了。接下来就是修改依赖属性的setter方法。

defineReactive(obj, prop, val) {
  let dep = new Dep()
  Object.defineProperty(obj, prop, {
    get() {
      console.log(`${prop} - 被读取!`)
      dep.getDeps()
      return val
    },
    set(newVal) {
      if (newVal == val) return
      val = newVal
      console.log(`${prop} - 被修改!`)
      dep.notify()  // 运行所有callback
    }
  })
}

运行看看


我们加多一个Watcher试试

new Watcher(man, "isGreat", () => {
  let {height, weight, wealth} = man
  if (height > 180 && weight > 70 && wealth > 100000) return "Great!"
  return "not good enough ..."
}, () => {
  console.log(`they say i am ${man.isGreat}`)
})


这就是vue中的一个依赖对应多个Watcher

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101580.html

相关文章

  • 深入浅出Vue响应式原理

    摘要:总结最后我们依照下图参考深入浅出,再来回顾下整个过程在后,会调用函数进行初始化,也就是过程,在这个过程通过转换成了的形式,来对数据追踪变化,当被设置的对象被读取的时候会执行函数,而在当被赋值的时候会执行函数。 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接,不过理解...

    yiliang 评论0 收藏0
  • 浅析Vue响应式原理(一)

    摘要:浅析响应式原理一的特点之一是响应式,视图随着数据的更新而更新,在视图中修改数据后实例中的数据也会同步更新。对于每个响应式数据,会有两个实例,第一个是在中的闭包遍历,用途显而易见。接收一个回调函数,会在重新求值且值更新后执行。 浅析Vue响应式原理(一) Vue的特点之一是响应式,视图随着数据的更新而更新,在视图中修改数据后Vue实例中的数据也会同步更新。内部借助依赖(下文中的Dep类)...

    lookSomeone 评论0 收藏0
  • 响应式数据数据依赖基本原理

    摘要:响应式数据响应式数据不是凭空出现的。对于对象而言,如果是之前不存在的属性,首先可以将进行响应化处理比如调用,然后将对具体属性定义监听比如调用函数,最后再去做赋值,可能具体的处理过程千差万别,但是内部实现的原理应该就是如此仅仅是猜测。 前言   首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励。   国内前端算...

    or0fun 评论0 收藏0
  • Vue原理】响应式原理 - 白话版

    摘要:所以我今后打算把每一个内容分成白话版和源码版。有什么错误的地方,感谢大家能够指出响应式系统我们都知道,只要在实例中声明过的数据,那么这个数据就是响应式的。什么是响应式,也即是说,数据发生改变的时候,视图会重新渲染,匹配更新为最新的值。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 V...

    gggggggbong 评论0 收藏0
  • Vue简单实现原理

    摘要:需要指令解析器,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。然后将模版中的变量替换成数据,渲染,将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据发生变动,收到通知,更新视图。 用了Vue也有两年时间了,一直以来都是只知其然,不知其所以然,为了能更好的使用Vue不被Vue所奴役,学习一下Vue底层的基本原理。 Vue官网有一段这样的介...

    luqiuwen 评论0 收藏0

发表评论

0条评论

SexySix

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<