简单实现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 相关文章推荐
JavaScript 验证浏览器是否支持javascript的方法小结
May 17 Javascript
jquery插件之easing使用
Aug 19 Javascript
JQuery中html()方法使用不当带来的陷阱
Apr 07 Javascript
jQuery $.get 的妙用 访问本地文本文件
Jul 12 Javascript
你必须知道的Javascript知识点之"深入理解作用域链"的介绍
Apr 23 Javascript
浅谈JavaScript的全局变量与局部变量
Jun 10 Javascript
jQuery ajax中使用confirm,确认是否删除的简单实例
Jun 17 Javascript
教大家轻松制作Bootstrap漂亮表格(table)
Dec 13 Javascript
Vue.js 中 axios 跨域访问错误问题及解决方法
Nov 21 Javascript
vue-cli3 DllPlugin 提取公用库的方法
Apr 24 Javascript
JS中call()和apply()的功能及用法实例分析
Jun 28 Javascript
JavaScript 类的封装操作示例详解
May 16 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
一台收音机,让一家人都笑逐颜开!
2020/08/21 无线电
php escape URL编码
2008/12/10 PHP
记录mysql性能查询过程的使用方法
2013/05/02 PHP
php多文件上传实现代码
2014/02/20 PHP
php中in_array函数用法分析
2014/11/15 PHP
php文件下载处理方法分析
2015/04/22 PHP
swoole和websocket简单聊天室开发
2017/11/18 PHP
PHP中in_array的隐式转换的解决方法
2018/03/06 PHP
用JavaScript和注册表脚本实现右键收藏Web页选中文本
2007/01/28 Javascript
Javascript attachEvent传递参数的办法
2009/12/14 Javascript
Javascript的闭包
2009/12/31 Javascript
Javascript中的变量使用说明
2010/05/18 Javascript
select标记美化--JS式插件、后期加载
2013/04/01 Javascript
HTML页面滚动时获取离页面顶部的距离2种实现方法
2013/09/05 Javascript
JavaScript中的迭代器和生成器详解
2014/10/29 Javascript
关于Vue组件库开发详析
2018/07/01 Javascript
vue子路由跳转实现tab选项卡
2019/07/24 Javascript
vue+element tabs选项卡分页效果
2020/06/29 Javascript
在vue中把含有html标签转为html渲染页面的实例
2019/10/28 Javascript
JS实现轮播图效果
2020/01/11 Javascript
Vue数组响应式操作及高阶函数使用代码详解
2020/08/01 Javascript
Python SQLite3数据库操作类分享
2014/06/10 Python
Python实现二分查找算法实例
2015/05/26 Python
使用Python下载歌词并嵌入歌曲文件中的实现代码
2015/11/13 Python
urllib和BeautifulSoup爬取维基百科的词条简单实例
2018/01/17 Python
python实现秒杀商品的微信自动提醒功能(代码详解)
2020/04/27 Python
QT5 Designer 打不开的问题及解决方法
2020/08/20 Python
The Kooples美国官方网站:为情侣提供的法国当代时尚品牌
2019/01/03 全球购物
PHP面试题附答案
2015/11/28 面试题
社区庆中秋节活动方案
2014/02/07 职场文书
大学生社团活动总结
2014/04/26 职场文书
技能比武方案
2014/05/21 职场文书
社区党建工作总结2015
2015/05/13 职场文书
奖学金主要事迹范文
2015/11/04 职场文书
2016优秀教师先进个人事迹材料
2016/02/25 职场文书
python超详细实现完整学生成绩管理系统
2022/03/17 Python