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 相关文章推荐
用javascript实现计算两个日期的间隔天数
Aug 14 Javascript
javascript 时间比较实现代码
Oct 28 Javascript
location.href语句与火狐不兼容的问题
Jul 04 Javascript
一个获取第n个元素节点的js函数
Sep 02 Javascript
Jquery中find与each方法用法实例
Feb 04 Javascript
jQuery复制表单元素附源码分享效果演示
Sep 30 Javascript
JS正则子匹配实例分析
Dec 22 Javascript
JavaScript动态绑定详解
Sep 14 Javascript
Angular数据绑定机制原理
Apr 17 Javascript
React 全自动数据表格组件——BodeGrid的实现思路
Jun 12 Javascript
解决LayUI加上form.render()下拉框和单选以及复选框不出来的问题
Sep 27 Javascript
Layui 解决表格异步调用后台分页的问题
Oct 26 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
Yii2框架可逆加密简单实现方法
2017/08/25 PHP
fix-ie5.js扩展在IE5下不能使用的几个方法
2007/08/20 Javascript
验证用户是否修改过页面的数据的实现方法
2008/09/26 Javascript
JS事件Event元素(兼容IE,Firefox,Chorme)
2012/11/01 Javascript
jQuery简单tab切换效果实现方法
2015/04/08 Javascript
js获取页面description的方法
2015/05/21 Javascript
JavaScript中的cacheStorage使用详解
2015/07/29 Javascript
深入理解angularjs过滤器
2016/05/25 Javascript
基于jQuery实现中英文切换导航条效果
2016/09/18 Javascript
JavaScript中数组的各种操作的总结(必看篇)
2017/02/13 Javascript
Angular4.x通过路由守卫进行路由重定向实现根据条件跳转到相应的页面(推荐)
2018/05/10 Javascript
基于vue展开收起动画的示例代码
2018/07/05 Javascript
vue 开发之路由配置方法详解
2019/12/02 Javascript
基于javascript处理二进制图片流过程详解
2020/06/08 Javascript
Vue中登录验证成功后保存token,并每次请求携带并验证token操作
2020/09/08 Javascript
超详细小程序定位地图模块全系列开发教学
2020/11/24 Javascript
在Mac OS上使用mod_wsgi连接Python与Apache服务器
2015/12/24 Python
浅谈Django REST Framework限速
2017/12/12 Python
python kmeans聚类简单介绍和实现代码
2018/02/23 Python
Python OpenCV处理图像之图像像素点操作
2018/07/10 Python
Python爬虫基础之XPath语法与lxml库的用法详解
2018/09/13 Python
Python实现12306火车票抢票系统
2019/07/04 Python
原生python实现knn分类算法
2019/10/24 Python
浅谈HTML5 FileReader分布读取文件以及其方法简介
2017/11/09 HTML / CSS
印尼最大的在线购物网站:MatahariMall.com
2016/08/26 全球购物
趣天网日本站:Qoo10 JP
2019/09/18 全球购物
客户代表自我评价范例
2013/09/24 职场文书
运动会领导邀请函
2014/02/05 职场文书
前处理班长职位说明书
2014/03/01 职场文书
理财计划书
2014/08/14 职场文书
劳动争议和解协议书范本
2014/11/20 职场文书
开平碉楼导游词
2015/02/06 职场文书
2016年国培心得体会及反思
2016/01/13 职场文书
改进工作作风心得体会
2016/01/23 职场文书
MySQL中VARCHAR与CHAR格式数据的区别
2021/05/26 MySQL
Log4j.properties配置及其使用
2021/08/02 Java/Android