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


Posted in Javascript onFebruary 18, 2019

开始

声明一个对象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

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

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

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

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

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

添加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'
})

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

看起来没什么问题,所依赖的属性如果变了,计算属性只要再被查看(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
  }
 })
}

运行看看

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

我们加多一个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中的依赖收集与响应的方法

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JCalendar 日历控件 v1.0 beta[兼容IE&Firefox] 有文档和例子
May 30 Javascript
国外Lightbox v2.03.3 最新版 下载
Oct 17 Javascript
javascript 鼠标悬浮图片显示原图 移出鼠标后原图消失(多图)
Dec 28 Javascript
jquery获取下拉列表的值为null的解决方法
Mar 18 Javascript
基于jquery自己写tab滑动门(通用版)
Oct 30 Javascript
React教程之Props验证的具体用法(Props Validation)
Sep 04 Javascript
深入理解Angular4订阅(Subscribe)与取消
Nov 22 Javascript
js获取html页面代码中图片地址的实现代码
Mar 05 Javascript
js中el表达式的使用和非空判断方法
Mar 28 Javascript
JavaScript中引用vs复制示例详析
Dec 06 Javascript
在vue中根据光标的显示与消失实现下拉列表
Sep 29 Javascript
JavaScript定时器使用方法详解
Mar 26 Javascript
vue实现的网易云音乐在线播放和下载功能案例
Feb 18 #Javascript
vue实现的微信机器人聊天功能案例【附源码下载】
Feb 18 #Javascript
vue-cli3环境变量与分环境打包的方法示例
Feb 18 #Javascript
JS实现集合的交集、补集、差集、去重运算示例【ES5与ES6写法】
Feb 18 #Javascript
JS基于开关思想实现的数组去重功能【案例】
Feb 18 #Javascript
JS实现点击li标签弹出对应的索引功能【案例】
Feb 18 #Javascript
Vue框架TypeScript装饰器使用指南小结
Feb 18 #Javascript
You might like
php smarty截取中文字符乱码问题?gb2312/utf-8
2011/11/07 PHP
fckeditor上传文件按日期存放及重命名方法
2015/05/22 PHP
wamp服务器访问php非常缓慢的解决过程
2015/07/01 PHP
Thinkphp 空操作、空控制器、命名空间(详解)
2017/05/05 PHP
PHP实现上传多图即时显示与即时删除的方法
2017/05/09 PHP
php魔法函数与魔法常量使用介绍
2017/07/23 PHP
使用JavaScript库还是自己写代码?
2010/01/28 Javascript
基于JQuery的多标签实现代码
2012/09/19 Javascript
了解了这些才能开始发挥jQuery的威力
2013/10/10 Javascript
JavaScript闭包详解
2015/02/02 Javascript
vue引入jq插件的实例讲解
2017/09/12 Javascript
bootstrap datetimepicker控件位置异常的解决方法
2017/11/23 Javascript
JS 中document.write()的用法和清空的原因浅析
2017/12/04 Javascript
vue组件挂载到全局方法的示例代码
2018/08/02 Javascript
Node.js中读取TXT文件内容fs.readFile()用法
2018/10/10 Javascript
[01:34]传奇从这开始 2016国际邀请赛中国区预选赛震撼开启
2016/06/26 DOTA
在VS Code上搭建Python开发环境的方法
2018/04/06 Python
Pandas实现数据类型转换的一些小技巧汇总
2018/05/07 Python
pandas 空数据处理方法详解
2019/11/02 Python
Python for i in range ()用法详解
2020/09/18 Python
python FTP批量下载/删除/上传实例
2019/12/22 Python
Python configparser模块封装及构造配置文件
2020/08/07 Python
python使用Windows的wmic命令监控文件运行状况,如有异常发送邮件报警
2021/01/30 Python
HTML5中视频音频的使用详解
2017/07/07 HTML / CSS
html5 标签
2009/07/16 HTML / CSS
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
美国婚礼和派对礼品网站:Kate Aspen(新娘送礼会、迎婴派对)
2018/03/28 全球购物
FragranceNet中文网:北美健康美容线上零售商
2020/08/26 全球购物
校园创业策划书
2014/01/14 职场文书
大学社团招新的通讯稿
2014/09/10 职场文书
团代会邀请函
2015/02/02 职场文书
2015年高校就业工作总结
2015/05/04 职场文书
教师节座谈会主持词
2015/07/03 职场文书
总经理致辞
2015/07/29 职场文书
给学校的建议书400字
2015/09/14 职场文书
民事纠纷协议书
2016/03/23 职场文书