简单实现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模块化编程(一)AMD规范(规范使用模块)
Jan 17 Javascript
javascript教程之不完整的继承(js原型链)
Jan 13 Javascript
JavaScript知识点总结(五)之Javascript中两个等于号(==)和三个等于号(===)的区别
May 31 Javascript
多功能jQuery树插件zTree实现权限列表简单实例
Jul 12 Javascript
node.js版本管理工具n无效的原理和解决方法
Nov 24 Javascript
node通过express搭建自己的服务器
Sep 30 Javascript
详解js模板引擎art template数组渲染的方法
Oct 09 Javascript
小程序中设置缓存过期的实现方法
Jan 14 Javascript
JS实现省市县三级下拉联动
Apr 10 Javascript
javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法
May 14 Javascript
Bootstrap table 服务器端分页功能实现方法示例
Jun 01 Javascript
vue项目两种方式实现竖向表格的思路分析
Apr 28 Vue.js
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
让你同时上传 1000 个文件 (二)
2006/10/09 PHP
PHP 工厂模式使用方法
2010/05/18 PHP
常用PHP封装分页工具类
2017/01/14 PHP
php实例化一个类的具体方法
2019/09/19 PHP
Yii框架自定义数据库操作组件示例
2019/11/11 PHP
xml 封装与解析(javascript和C#中)
2009/07/26 Javascript
Javascript 读取操作Sql中的Xml字段
2014/10/09 Javascript
jquery实现搜索框常见效果的方法
2015/01/22 Javascript
PHP中CURL的几个经典应用实例
2015/01/23 Javascript
javascript实现rgb颜色转换成16进制格式
2015/07/10 Javascript
不用一句js代码初始化组件
2016/01/27 Javascript
详解Angularjs filter过滤器
2016/02/06 Javascript
JavaScript模拟鼠标右键菜单效果
2020/12/08 Javascript
浅析JS原型继承与类的继承
2016/04/07 Javascript
浅谈jQuery中ajaxPrefilter的应用
2016/08/01 Javascript
很实用的js选项卡切换效果
2016/08/12 Javascript
Bootstarp基本模版学习教程
2017/02/01 Javascript
详解vue+vueRouter+webpack的简单实例
2017/06/17 Javascript
详解webpack分包及异步加载套路
2017/06/29 Javascript
详解React-Native全球化多语言切换工具库react-native-i18n
2017/11/03 Javascript
搭建vue开发环境
2018/07/19 Javascript
解决layui laydate 时间控件一闪而过的问题
2019/09/28 Javascript
JQuery实现折叠式菜单的详细代码
2020/06/03 jQuery
Vue中使用JsonView来展示Json树的实例代码
2020/11/16 Javascript
[02:56]DOTA2亚洲邀请赛 VG出场战队巡礼
2015/02/07 DOTA
Python爬虫番外篇之Cookie和Session详解
2017/12/27 Python
Python 基于wxpy库实现微信添加好友功能(简洁)
2019/11/29 Python
Amara美国站:英国高端家居礼品网站,世界各地的奢侈家具品牌
2017/07/26 全球购物
Ralph Lauren英国官方网站:Ralph Lauren UK
2018/04/03 全球购物
一份Java笔试题
2012/02/21 面试题
mysql有关权限的表都有哪几个
2015/04/22 面试题
汽修专业自荐信
2014/07/07 职场文书
党员个人对照检查材料思想汇报
2014/09/16 职场文书
2014年扫黄打非工作总结
2014/12/03 职场文书
Python re.sub 反向引用的实现
2021/07/07 Python
微信小程序 根据不同用户切换不同TabBar
2022/04/21 Javascript