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 相关文章推荐
Raphael带文本标签可拖动的图形实现代码
Feb 20 Javascript
关于jQuery中.attr()和.prop()的问题探讨
Sep 06 Javascript
JS禁用浏览器退格键实现思路及代码
Oct 29 Javascript
使用JavaScript进行进制转换将字符串转换为十进制
Sep 21 Javascript
详解Node.js包的工程目录与NPM包管理器的使用
Feb 16 Javascript
js评分组件使用详解
Jun 06 Javascript
js 发布订阅模式的实例讲解
Sep 10 Javascript
vue.js单文件组件中非父子组件的传值实例
Sep 13 Javascript
关于vue编译版本引入的问题的解决
Sep 17 Javascript
深度了解vue.js中hooks的相关知识
Jun 14 Javascript
javascript实现计算器功能
Mar 30 Javascript
Vue中函数防抖节流的理解及应用实现
Apr 24 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源码之explode使用说明
2011/08/05 PHP
浏览器关闭后,能继续执行的php函数(ignore_user_abort)
2012/08/01 PHP
php实现memcache缓存示例讲解
2013/12/04 PHP
php使用mkdir创建多级目录入门例子
2014/05/10 PHP
php实现模拟post请求用法实例
2015/07/11 PHP
php生成复杂验证码(倾斜,正弦干扰线,黏贴,旋转)
2018/03/12 PHP
Laravel 集成 Geetest验证码的方法
2018/05/14 PHP
PHP正则表达式函数preg_replace用法实例分析
2020/06/04 PHP
入门基础学习 ExtJS笔记(一)
2010/11/11 Javascript
固定背景实现的背景滚动特效示例分享
2013/05/19 Javascript
JS getAttribute和setAttribute(取得和设置属性)的使用介绍
2013/07/10 Javascript
javaScript实现浮点数转十六进制字符
2013/10/29 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史
2015/01/09 Javascript
jquery插件validation实现验证身份证号等
2015/06/04 Javascript
Ionic默认的Tabs模板使用实例
2016/08/29 Javascript
JavaScript中setTimeout的那些事儿
2016/11/14 Javascript
利用jquery实现验证输入的是否是数字、小数,包含保留几位小数
2016/12/07 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
浅谈angularjs中响应回车事件
2017/04/24 Javascript
vue form 表单提交后刷新页面的方法
2018/09/04 Javascript
element-ui upload组件多文件上传的示例代码
2018/10/17 Javascript
JavaScript中的函数申明、函数表达式、箭头函数
2019/12/06 Javascript
vue vant中picker组件的使用
2020/11/03 Javascript
[02:43]2014DOTA2国际邀请赛 官方Alliance战队纪录片
2014/07/14 DOTA
Python+MongoDB自增键值的简单实现
2016/11/04 Python
python机器学习之神经网络(二)
2017/12/20 Python
numpy添加新的维度:newaxis的方法
2018/08/02 Python
python rsync服务器之间文件夹同步脚本
2019/08/29 Python
TensorFlow Autodiff自动微分详解
2020/07/06 Python
Python2.x与3​​.x版本有哪些区别
2020/07/09 Python
通过python-pptx模块操作ppt文件的方法
2020/12/26 Python
使用bandit对目标python代码进行安全函数扫描的案例分析
2021/01/27 Python
利用纯css3实现的文字亮光特效的代码演示
2014/11/27 HTML / CSS
美国修容界大佬创建的个人美妆品牌:Kevyn Aucoin Beauty
2018/12/12 全球购物
项目采购员岗位职责
2014/04/15 职场文书
uniapp引入支付宝原生扫码插件步骤详解
2022/07/23 Javascript