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 相关文章推荐
浏览器窗口大小变化时使用resize事件对框架不起作用的解决方法
May 11 Javascript
setinterval()与clearInterval()JS函数的调用方法
Jan 21 Javascript
JavaScript取得WEB安全颜色列表的方法
Jul 14 Javascript
深入浅析jQuery对象$.html
Aug 22 Javascript
jQuery快速实现商品数量加减的方法
Feb 06 Javascript
Bootstrap输入框组件简单实现代码
Mar 06 Javascript
angularJS的radio实现单项二选一的使用方法
Feb 28 Javascript
使用node.js实现微信小程序实时聊天功能
Aug 13 Javascript
VuePress 快速踩坑小结
Feb 14 Javascript
Vue实现可移动水平时间轴
Jun 29 Javascript
vue移动端下拉刷新和上滑加载
Oct 27 Javascript
nuxt引入组件和公共样式的操作
Nov 05 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生成静态页面详解
2006/11/19 PHP
组合算法的PHP解答方法
2012/02/04 PHP
php打造属于自己的MVC框架
2012/03/07 PHP
浅析is_writable的php实现
2013/06/18 PHP
php中get_object_vars()方法用法实例
2015/02/08 PHP
PHP中call_user_func_array回调函数的用法示例
2016/11/26 PHP
php中文语义分析实现方法示例
2019/09/28 PHP
JavaScript 核心参考教程 内置对象
2009/10/13 Javascript
js 居中漂浮广告
2010/03/21 Javascript
WEB高性能开发之疯狂的HTML压缩
2010/06/19 Javascript
jQuery中使用Ajax获取JSON格式数据示例代码
2013/11/26 Javascript
举例讲解Node.js中的Writable对象
2015/07/29 Javascript
第七篇Bootstrap表单布局实例代码详解(三种表单布局)
2016/06/21 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(二)
2016/09/14 Javascript
bootstrap组件之按钮式下拉菜单小结
2017/01/19 Javascript
原生JS实现幻灯片
2017/02/22 Javascript
结合Vue控制字符和字节的显示个数的示例
2018/05/17 Javascript
vue中使用微信公众号js-sdk踩坑记录
2019/03/29 Javascript
vue.js实现备忘录demo
2019/06/26 Javascript
小程序Scroll-view上拉滚动刷新数据
2020/06/21 Javascript
vue+AI智能机器人回复功能实现
2020/07/16 Javascript
Python实现截屏的函数
2015/07/26 Python
Python教程之全局变量用法
2016/06/27 Python
一个基于flask的web应用诞生 组织结构调整(7)
2017/04/11 Python
python音频处理用到的操作的示例代码
2017/10/27 Python
EM算法的python实现的方法步骤
2018/01/02 Python
快速了解python leveldb
2018/01/18 Python
基于Python开发chrome插件的方法分析
2018/07/07 Python
Django多进程滚动日志问题解决方案
2019/12/17 Python
Numpy一维线性插值函数的用法
2020/04/22 Python
10款最佳Python开发工具推荐,每一款都是神器
2020/10/15 Python
一文彻底解决HTML5页面中长按保存图片功能
2019/06/10 HTML / CSS
Peter Millar官网:美国高档生活服饰品牌
2018/07/02 全球购物
药学专业毕业生求职信
2013/10/20 职场文书
学校大课间活动方案
2014/01/30 职场文书
银行青年文明号事迹材料
2014/05/31 职场文书