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 相关文章推荐
用apply让javascript函数仅执行一次的代码
Jun 27 Javascript
jQuery.autocomplete 支持中文输入(firefox)修正方法
Mar 10 Javascript
js禁止页面复制功能禁用页面右键菜单示例代码
Aug 29 Javascript
jQuery simplePage+AJAX plus分页插件用法实例
Feb 17 Javascript
jQuery事件绑定方法学习总结(推荐)
Nov 21 Javascript
bootstrap提示标签、提示框实现代码
Dec 28 Javascript
微信小程序 Button 组件详解及简单实例
Jan 10 Javascript
vue mint-ui 实现省市区街道4级联动示例(仿淘宝京东收货地址4级联动)
Oct 16 Javascript
微信小程序实现收藏与取消收藏切换图片功能
Aug 03 Javascript
react native 获取地理位置的方法示例
Aug 28 Javascript
详解auto-vue-file:一个自动创建vue组件的包
Apr 26 Javascript
解决layui弹框失效的问题
Sep 09 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
mysql建立外键
2006/11/25 PHP
《PHP编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
2010/11/01 PHP
php中使用exec,system等函数调用系统命令的方法(不建议使用,可导致安全问题)
2012/09/07 PHP
thinkphp模板的包含与渲染实例分析
2014/11/26 PHP
PHP执行SQL文件并将SQL文件导入到数据库
2015/09/17 PHP
改变隐藏的input中value值的方法
2014/03/19 Javascript
js验证IP及子网掩码的合法性有效性示例
2014/04/30 Javascript
绑定回车enter事件代码
2014/05/18 Javascript
js脚本实现数据去重
2014/11/27 Javascript
百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
2016/02/19 Javascript
jQuery实现的tab标签切换效果示例
2016/09/05 Javascript
jquery实现的回旋滚动效果完整实例【附demo源码下载】
2016/09/20 Javascript
js实现下一页页码效果
2017/03/07 Javascript
微信小程序之购物车功能
2020/09/23 Javascript
Angular 2.x学习教程之结构指令详解
2017/05/25 Javascript
vue2.0+vuex+localStorage代办事项应用实现详解
2018/05/31 Javascript
vue实现点击当前标签高亮效果【推荐】
2018/06/22 Javascript
vue favicon设置以及动态修改favicon的方法
2018/12/21 Javascript
[原创]微信小程序获取网络类型的方法示例
2019/03/01 Javascript
Node.js Domain 模块实例详解
2020/03/18 Javascript
解决父组件将子组件作为弹窗调用只执行一次created的问题
2020/07/24 Javascript
vue 组件基础知识总结
2021/01/26 Vue.js
Python函数返回值实例分析
2015/06/08 Python
python3+PyQt5使用数据库窗口视图
2018/04/24 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
2021/01/18 Python
CSS实现的一闪而过的图片闪光效果
2014/04/23 HTML / CSS
HTML5 Canvas——用路径描画线条实例介绍
2013/06/09 HTML / CSS
澳大利亚100%丝绸多彩度假装商店:TheSwankStore
2019/09/04 全球购物
建筑自我鉴定
2013/10/19 职场文书
2014年自我评价
2014/01/04 职场文书
农村门前三包责任书
2014/07/25 职场文书
2015年重阳节慰问信
2015/03/23 职场文书
2015年超市员工工作总结
2015/05/04 职场文书
2016秋季校长开学典礼致辞
2015/11/26 职场文书
实用求职信模板范文
2019/05/13 职场文书
手把手教你怎么用Python实现zip文件密码的破解
2021/05/27 Python