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 相关文章推荐
基于jquery的地址栏射击游戏代码
Mar 10 Javascript
利用jQuery实现可以编辑的表格
May 26 Javascript
js实现点击左右按钮轮播图片效果实例
Jan 29 Javascript
JavaScript中的substr()方法使用详解
Jun 06 Javascript
js判断图片加载完成后获取图片实际宽高的方法
Feb 25 Javascript
jquery中live()方法和bind()方法区别分析
Jun 23 Javascript
Bootstrap CSS组件之导航(nav)
Dec 17 Javascript
js中setTimeout的妙用--防止循环超时
Mar 06 Javascript
Angular客户端请求Rest服务跨域问题的解决方法
Sep 19 Javascript
Vue中 v-if 和v-else-if页面加载出现闪现的问题及解决方法
Oct 12 Javascript
vue组件通信传值操作示例
Jan 08 Javascript
Vue响应式原理Observer、Dep、Watcher理解
Jun 06 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
如何限制访问者的ip(PHPBB的代码)
2006/10/09 PHP
php 仿Comsenz安装效果代码打包提供下载
2010/05/09 PHP
PHP魔术方法以及关于独立实例与相连实例的全面讲解
2016/10/18 PHP
php实现的统计字数函数定义与使用示例
2017/07/26 PHP
Kindeditor编辑器添加图片上传水印功能(php代码)
2017/08/03 PHP
PHP中递归的实现实例详解
2017/11/14 PHP
密码框显示提示文字jquery示例
2013/08/29 Javascript
javascript转换日期字符串为Date日期对象的方法
2015/02/13 Javascript
vue select组件的使用与禁用实现代码
2018/04/10 Javascript
一个Vue页面的内存泄露分析详解
2018/06/25 Javascript
vue.js内置组件之keep-alive组件使用
2018/07/10 Javascript
微信小程序之自定义组件的实现代码(附源码)
2018/08/02 Javascript
在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法
2018/10/25 Javascript
关于element-ui的隐藏组件el-scrollbar的使用
2019/05/29 Javascript
vue中改变滚动条样式的方法
2020/03/03 Javascript
JS实现网页时钟特效
2020/03/25 Javascript
vue使用element-ui实现表单验证
2020/12/13 Vue.js
Python3.6 Schedule模块定时任务(实例讲解)
2017/11/09 Python
使用requests库制作Python爬虫
2018/03/25 Python
PyCharm代码提示忽略大小写设置方法
2018/10/28 Python
python实现给scatter设置颜色渐变条colorbar的方法
2018/12/13 Python
Python django框架应用中实现获取访问者ip地址示例
2019/05/17 Python
Python 判断时间是否在时间区间内的实例
2020/05/16 Python
python中如何写类
2020/06/29 Python
浅析Python 抽象工厂模式的优缺点
2020/07/13 Python
Html5游戏开发之乒乓Ping Pong游戏示例(三)
2013/01/21 HTML / CSS
Avène雅漾美国官方网站:敏感肌肤护理专家
2016/10/24 全球购物
德国团购网站:Groupon德国
2018/03/13 全球购物
投资建议书模板
2014/05/12 职场文书
2014年社区工会工作总结
2014/12/18 职场文书
归元寺导游词
2015/02/06 职场文书
水电工岗位职责
2015/02/14 职场文书
台风停课通知
2015/04/24 职场文书
通讯稿格式及范文
2015/07/22 职场文书
2016形势与政策学习心得体会
2016/01/12 职场文书
求职信:会计求职的写作技巧
2019/04/24 职场文书