Vue.js 中的 $watch使用方法


Posted in Javascript onMay 25, 2017

这两天学习了Vue.js 中的 $watch这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记。

Vue.js 中的 $watch使用方法

github 源码 

Observer, Watcher, vm 可谓 Vue 中比较重要的部分,检测数据变动后视图更新的重要环节。下面我们来看看 如何实现一个简单的 $watch 功能,当然Vue 中使用了很多优化手段,在本文中暂不一一讨论。

例子:

// 创建 vm
let vm = new Vue({
 data: 'a'
})

// 键路径
vm.$watch('a.b.c', function () {
 // 做点什么
})

先阐明在这个 demo 以及Vue 中,它们的关系:

vm 调用 $watch 后,首先调用 observe 函数 创建 Observer 实例观察数据,Observer 又创建 Dep , Dep 用来维护订阅者。然后创建 Watcher 实例提供 update 函数。一旦数据变动,就层层执行回调函数。

Vue.js 中的 $watch使用方法

Observer和observe

递归调用 observe 函数创建 Observer。在创建 Observer 的过程中,使用 Object.defineProperty() 函数为其添加 get set 函数, 并创建 Dep 实例。

export function observe (val) {
 if (!val || typeof val !== 'object') {
  return
 }
 return new Observer(val)
}
function defineReactive (obj, key, val) {
 var dep = new Dep()

 var property = Object.getOwnPropertyDescriptor(obj, key)
 // 是否允许修改
 if (property && property.configurable === false) {
  return
 }

 // 获取定义好的 get set 函数
 var getter = property && property.get
 var setter = property && property.set

 var childOb = observe(val)
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: () => {
   var value = getter ? getter.call(obj) : val
   // 说明是 Watcher 初始化时获取的, 就添加订阅者
   if (Dep.target) {
    dep.depend()
    if (childOb) {
     childOb.dep.depend()
    }
    // if isArray do some....
   }
   return value
  },
  set: (newVal) => {
   var value = getter ? getter.call(obj) : val
   if (value === newVal) {
    return
   }
   if (setter) {
    setter.call(obj, newVal)
   } else {
    val = newVal
   }
   childOb = observe(newVal)
   dep.notify()
  }
 })
}

你可能会疑问 Dep.target 是个什么鬼??

答案是:Watcher, 我们接下来看

Dep

export default function Dep () {
 this.subs = []
}

// 就是你!!~
Dep.target = null 

// 添加订阅者
Dep.prototype.addSub = function (sub) {
 this.subs.push(sub)
}

// 添加依赖
Dep.prototype.depend = function () {
 Dep.target.addDep(this)
}

// 通知订阅者:要更新啦~
Dep.prototype.notify = function () {
 this.subs.forEach(sub => sub.update())
}

Watcher

为了给每个数据添加订阅者,我们想到的办法是在数据的 get 函数中, 但是 get 函数会调用很多次呀~。。。 肿么办?那就给 Dep 添加个参数 target

export default function Watcher (vm, expOrFn, cb) {
 this.cb = cb
 this.vm = vm
 this.expOrFn = expOrFn
 this.value = this.get()
}

Watcher.prototype.get = function () {
 Dep.target = this
 const value = this.vm._data[this.expOrFn]
 // 此时 target 有值,此时执行到了上面的 defineReactive 函数中 get 函数。就添加订阅者
 Dep.target = null
 // 为了不重复添加 就设置为 null
 return value
}

Vue Instance

在 Vue Instance 做得最多的事情就是初始化 State, 添加函数等等。

// Vue 实例
export default function Vue(options) {
 this.$options = options
 this._initState()
}

// 初始化State
Vue.prototype._initState = function () {
 let data = this._data = this.$options.data
 Object.keys(data).forEach(key => this._proxy(key))
 observe(data, this)
}

// $watch 函数,
Vue.prototype.$watch = function (expOrFn, fn, options) {
 new Watcher(this, expOrFn, fn)
}

总结

至此,我们已经实现了一个简单的 $watch 函数, Object.defineProperty() 函数可谓是举足轻重, 因此不支持该函数的浏览器, Vue 均不支持。

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

Javascript 相关文章推荐
In Javascript Class, how to call the prototype method.(three method)
Jan 09 Javascript
jQuery编写widget的一些技巧分享
Oct 28 Javascript
js验证模型自我实现的具体方法
Jun 21 Javascript
js监听键盘事件示例代码
Jul 26 Javascript
javascript实现单击和双击并存的方法
Dec 13 Javascript
javascript倒计时效果实现
Nov 12 Javascript
Angular.js回顾ng-app和ng-model使用技巧
Apr 26 Javascript
AngularJs学习第五篇从Controller控制器谈谈$scope作用域
Jun 08 Javascript
关于JS中的方法是否加括号的问题
Jul 27 Javascript
js canvas画布实现高斯模糊效果
Nov 27 Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
May 23 Javascript
vue任意关系组件通信与跨组件监听状态vue-communication
Oct 18 Javascript
详解Javascript获取缓存和清除缓存API
May 25 #Javascript
Angularjs 实现动态添加控件功能
May 25 #Javascript
JavaScript实现自动跳转文本功能
May 25 #Javascript
angularjs项目的页面跳转如何实现(5种方法)
May 25 #Javascript
AngularJS基于factory创建自定义服务的方法详解
May 25 #Javascript
AngularJS读取JSON及XML文件的方法示例
May 25 #Javascript
bootstrap+jQuery实现的动态进度条功能示例
May 25 #jQuery
You might like
精通php的十大要点(上)
2009/02/04 PHP
10条PHP高级技巧[修正版]
2011/08/02 PHP
php获得url参数中具有&的值的方法
2014/03/05 PHP
php实现专业获取网站SEO信息类实例
2015/04/02 PHP
PHP使用反向Ajax技术实现在线客服系统详解
2019/07/01 PHP
超级24小时弹窗代码 24小时退出弹窗代码 100%弹窗代码(IE only)
2010/06/11 Javascript
用jquery实现的一个超级简单的下拉菜单
2014/05/18 Javascript
详细分析使用AngularJS编程中提交表单的方式
2015/06/19 Javascript
再JavaScript的jQuery库中编写动画效果的指南
2015/08/13 Javascript
js实现表单检测及表单提示的方法
2015/08/14 Javascript
javascript数据结构之双链表插入排序实例详解
2015/11/25 Javascript
js去字符串前后空格的实现方法
2016/02/26 Javascript
深入理解JQuery中的事件与动画
2016/05/18 Javascript
简单实现jQuery上传图片显示预览功能
2020/06/29 jQuery
简单说说angular.json文件的使用
2018/10/29 Javascript
JS div匀速移动动画与变速移动动画代码实例
2019/03/26 Javascript
微信小程序访问豆瓣电影api的实现方法
2019/03/31 Javascript
javascript头像上传代码实例
2019/09/28 Javascript
Node.js创建一个Express服务的方法详解
2020/01/06 Javascript
[01:08:43]DOTA2-DPC中国联赛定级赛 Phoenix vs DLG BO3第一场 1月9日
2021/03/11 DOTA
用Python中的__slots__缓存资源以节省内存开销的方法
2015/04/02 Python
python 3利用BeautifulSoup抓取div标签的方法示例
2017/05/28 Python
python函数超时自动退出的实操方法
2020/12/28 Python
python 邮件检测工具mmpi的使用
2021/01/04 Python
CSS3 Columns分列式布局方法简介
2014/05/03 HTML / CSS
HTML5 Canvas如何实现纹理填充与描边(Fill And Stroke)
2013/07/15 HTML / CSS
韩国流行时尚女装网站:Dintchina(中文)
2018/07/19 全球购物
环境科学专业个人求职信
2013/09/26 职场文书
信息工程学院毕业生推荐信
2013/11/05 职场文书
高三毕业典礼演讲稿
2014/05/13 职场文书
对学校的意见和建议
2015/06/04 职场文书
返乡农民工证明
2015/06/24 职场文书
Python使用OpenCV和K-Means聚类对毕业照进行图像分割
2021/06/11 Python
快速学习Oracle触发器和游标
2021/06/30 Oracle
windows安装 redis 6.2.6最新步骤详解
2022/04/26 Redis
Mysql 数据库中的 redo log 和 binlog 写入策略
2022/04/26 MySQL