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 相关文章推荐
XMLHTTPRequest的属性和方法简介
Nov 23 Javascript
javascript Event对象详解及使用示例
Nov 22 Javascript
jQuery Validate 验证,校验规则写在控件中的具体实例
Feb 27 Javascript
基于javascript显示当前时间以及倒计时功能
Mar 18 Javascript
纯js模仿windows系统日历
Feb 04 Javascript
js仿微信公众平台打标签功能
Apr 08 Javascript
利用Vue.js实现求职在线之职位查询功能
Jul 03 Javascript
vue实现文章内容过长点击阅读全文功能的实例
Dec 28 Javascript
vue实现底部菜单功能
Jul 24 Javascript
js中自定义react数据验证组件实例详解
Oct 19 Javascript
JavaScript RegExp 对象用法详解
Sep 24 Javascript
JS回调函数简单易懂的入门实例分析
Sep 29 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中修改浏览器的User-Agent来伪装你的浏览器和操作系统
2011/07/29 PHP
php用正则表达式匹配中文实例详解
2013/11/06 PHP
PHP中的Memcache详解
2014/04/05 PHP
php显示指定目录下子目录的方法
2015/03/20 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
jquery 表单进行客户端验证demo
2009/08/24 Javascript
在chrome浏览器中,防止input[text]和textarea在聚焦时出现黄色边框的解决方法
2011/05/24 Javascript
使用javascript做的一个随机点名程序
2014/02/13 Javascript
js手机号批量滚动抽奖实现代码
2020/04/17 Javascript
微信小程序 安全包括(框架、功能模块、账户使用)详解
2017/01/16 Javascript
jQuery页面弹出框实现文件上传
2017/02/09 Javascript
nuxt+axios解决前后端分离SSR的示例代码
2017/10/24 Javascript
javascript浏览器用户代理检测脚本实现方法
2017/10/27 Javascript
JavaScript中发出HTTP请求最常用的方法
2018/07/12 Javascript
vue 实现element-ui中的加载中状态
2020/11/11 Javascript
Python生成随机密码的方法
2017/06/16 Python
pandas中去除指定字符的实例
2018/05/18 Python
python 运用Django 开发后台接口的实例
2018/12/11 Python
Python创建字典的八种方式
2019/02/27 Python
python远程连接MySQL数据库
2019/04/19 Python
Pandas替换及部分替换(replace)实现流程详解
2020/10/12 Python
HTML5 Web缓存和运用程序缓存(cookie,session)
2018/01/11 HTML / CSS
澳大利亚正品化妆品之家:Cosmetic Capital
2017/07/03 全球购物
什么是serialVersionUID
2016/03/04 面试题
金智子午JAVA面试题
2015/09/04 面试题
Linux不知道文件后缀名怎么判断文件类型
2012/04/26 面试题
小学国庆节活动方案
2014/02/11 职场文书
软环境建设心得体会
2014/09/09 职场文书
高校师德师风自我剖析材料
2014/09/29 职场文书
村党支部书记四风问题个人对照检查材料思想汇报
2014/10/06 职场文书
2014年统计工作总结
2014/11/21 职场文书
先进个人材料怎么写
2014/12/30 职场文书
《山中访友》教学反思
2016/02/24 职场文书
解决python存数据库速度太慢的问题
2021/04/23 Python
vue-cli4.5.x快速搭建项目
2021/05/30 Vue.js
详解Python描述符的工作原理
2021/06/11 Python