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 相关文章推荐
ext实现完整的登录代码
Aug 08 Javascript
JQUBAR1.1 jQuery 柱状图插件发布
Nov 28 Javascript
解决js中window.open弹出的是上次的缓存页面问题
Dec 29 Javascript
jquery选择器大全 全面详解jquery选择器
Mar 06 Javascript
JavaScript代码轻松实现网页内容禁止复制(代码简单)
Oct 23 Javascript
window.onerror()的用法与实例分析
Jan 27 Javascript
浅谈JavaScript 中有关时间对象的方法
Aug 15 Javascript
vue插件tab选项卡使用小结
Oct 27 Javascript
jQuery EasyUI之验证框validatebox实例详解
Apr 10 jQuery
vue中实现滚动加载更多的示例
Nov 08 Javascript
动态加载、移除js/css文件的示例代码
Mar 20 Javascript
ant design实现圈选功能
Dec 17 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
无线电广播与收音机发展的历史回眸
2021/03/02 无线电
ThinkPHP模板引擎之导入资源文件方法详解
2014/06/18 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
Laravel如何实现自动加载类
2019/10/14 PHP
jQuery JSON的解析方式分享
2011/04/05 Javascript
jquery $.each() 使用小探
2013/08/23 Javascript
Javascript中常见的校验如域名、手机、邮箱等等
2014/01/02 Javascript
JavaScript闭包实例讲解
2014/04/22 Javascript
javascript实现按回车键切换焦点
2015/02/09 Javascript
js控制li的隐藏和显示实例代码
2016/10/15 Javascript
JS button按钮实现submit按钮提交效果
2016/11/01 Javascript
JS冒泡事件与事件捕获实例详解
2016/11/25 Javascript
JS键盘版计算器的制作方法
2016/12/03 Javascript
js实现下拉菜单效果
2017/03/01 Javascript
详解vue mint-ui源码解析之loadmore组件
2017/10/11 Javascript
json字符串传到前台input的方法
2018/08/06 Javascript
Jquery的autocomplete插件用法及参数讲解
2019/03/12 jQuery
vue+eslint+vscode配置教程
2019/08/09 Javascript
解决 window.onload 被覆盖的问题方法
2020/01/14 Javascript
vue在图片上传的时候压缩图片
2020/11/18 Vue.js
vue仿携程轮播图效果(滑动轮播,下方高度自适应)
2021/02/11 Vue.js
Python使用sort和class实现的多级排序功能示例
2018/08/15 Python
django mysql数据库及图片上传接口详解
2019/07/18 Python
Python 脚本拉取 Docker 镜像问题
2019/11/10 Python
使用Python爬虫库BeautifulSoup遍历文档树并对标签进行操作详解
2020/01/25 Python
美国农场鲜花速递:The Bouqs
2018/07/13 全球购物
SIXPAD智能健身仪英国官网:革命性的训练装备品牌
2018/09/27 全球购物
白俄罗斯大卖场:21vek.by
2019/07/25 全球购物
什么时候需要进行强制类型转换
2016/09/03 面试题
社会体育专业大学生职业生涯规划书
2014/09/17 职场文书
颐和园英文导游词
2015/01/30 职场文书
介绍信的写法
2015/01/31 职场文书
实习生辞职信范文
2015/03/02 职场文书
房租涨价通知
2015/04/23 职场文书
2015暑期社会实践调查报告
2015/07/14 职场文书
详解RedisTemplate下Redis分布式锁引发的系列问题
2021/04/27 Redis