通过上一篇文章《Vue源码之渲染watcher》我们学习到了每个组件实例初始化时都会创建一个渲染watcher来监控页面引用到的响应式数据的变动,一旦数据发生变化,就会通知渲染watcher来重新生成虚拟DOM,做diff,update,然后patch来更新页面。

今天我们将会学习下vue框架用到的第三个watcher - 用户watcher。下面回忆下这三个watcher的简介:

  • 计算属性watcher: vue会为我们在computed选项写的每个我们自定义的计算属性创建一个计算属性watcher,该watcher会在依赖的响应式数据变化时将计算属性标志位设置成dirty,使得页面在下次更新时调用计算属性函数进行求值。
  • 渲染watcher: vue会为每个组件创建一个渲染watcher来在依赖的响应式数据状态发生变化时重新渲染页面
  • 用户watcher: vue会为我们在watch选项写的每个要监控的属性创建一个watcher来在其变化时执行提供的回调函数

既然用户watcher是根据我们编写的watch选项而创建的,所以我们下面先简单介绍下watch的用法。

1. watch用法简介

我们在编写组件代码时,如果我们需要监控某个响应式属性的变化,我们会在watch选项下面实现相关的监听函数。

标准的写法应该是下面这个样子的

watch: {counter: {handler: function (newVal, oldVal) {console.log(newVal, oldVal);},},
},

如果不需要提供immediate,deep等选项的话,可以简化写成下面这样

watch: {counter: function (newVal, oldVal) {console.log(newVal, oldVal);},},

一个完整的vue实例初始化例子如下

<!DOCTYPE html>
<html lang="en"><head><script src="vue.js"></script></head><body><div id="app"><div>{{counter}}</div><button @click="increase">Increase</button></div><script>const vm = new Vue({el: "#app",data: {counter: 1,},methods: {increase() {this.counter += 1;},},watch: {counter: {handler: function (newVal, oldVal) {console.log(newVal, oldVal);},},},});</script></body>
</html>

2. 用户watcher源码分析

下面开始分析下我们写的watch选项是怎么生成用户watcher的。

我们编写vue实例或者组件实例的时候,比如上面的创建vue实例中的例子,在写好watch选项之后,vue会将这些选项放到options参数并传递给他_init方法来对实例进行初始化。然后会经历一系列的初始化函数调用流程。

// core/instance/index.js文件
function Vue(options) {this._init(options);
}// core/instances/init.js文件
Vue.prototype._init = function (options?: Object) {const vm: Component = this;...vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm)...initState(vm);
}// core/instance/sate.js文件
export function initState(vm: Component) {vm._watchers = []const opts = vm.$options....if (opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)}}

在_init方法中会对我们传入的选项配置进行处理,然后放入到实例的$options选项中。到了initState时,就会以我们编写的watch配置选项作为参数调用initWatch方法对用户watcher进行初始化。

function initWatch(vm: Component, watch: Object) {for (const key in watch) {const handler = watch[key]...createWatcher(vm, key, handler)}
}

这里会直接调用createWatcher方法

function createWatcher(vm: Component,expOrFn: string | Function,handler: any,options?: Object
) {if (isPlainObject(handler)) {options = handlerhandler = handler.handler}...return vm.$watch(expOrFn, handler, options)
}

这里留意下handler处理部分,如果我们的watch的回调函数写法是标准带handler的那种写法的话,需要将配置项里面的handler拿出来作为本函数里面的handler函数。

跟着就是调用vue的原型函数$watch方法

  Vue.prototype.$watch = function (expOrFn: string | Function,cb: any,options?: Object): Function {const vm: Component = this...options = options || {}options.user = trueconst watcher = new Watcher(vm, expOrFn, cb, options)...}

这里注意options.user = true,这就是我们这个要创建的watcher叫做用户watcher的原因。

在调用Watcher构造函数的时候,我们这次提供了四个参数,值得留意的是,相比上两篇文章分析的渲染watcher和计算属性watcher,我们这次提供了第三个参数,即回调函数,该函数将会在依赖的响应式属性变化时被watcher执行。而这个cb,就是我们上面的handler,也就是我们自己写的那个watch的handler函数。

下面看下用户watche的构造过程

export default class Watcher {constructor(vm: Component,expOrFn: string | Function,cb: Function,options?: ?Object,isRenderWatcher?: boolean) {this.vm = vm;...// optionsif (options) {...this.user = !!options.user;} this.cb = cb;...if (typeof expOrFn === "function") {this.getter = expOrFn;} else {this.getter = parsePath(expOrFn);...}this.value = this.lazy ? undefined : this.get();}

Watcher的构造函数其实我们看了很多遍了,只是为了方便分析,针对不同使用情况下的watcher,我们就会把和它不相关的代码给省略掉。这里也一样,我们这里只保留和今天学习用户Watcher相关的核心代码。

  • 首先,这里把用户watcher的标识位存储到user成员变量中
  • 然后,将我们编写的handler函数保存到cb成员变量中
  • 跟着,因为我们提供的expOrFn是’counter’或者’counter.total’这样的字符串,所以会调用parsePath来返回一个getter函数,该函数会接受一个对象,然后把对象里面的expOrFn为key的对象返回,比如接收vm对象,然后将下面的counter属性的的值给返回。该函数我们在《Vue源码分析基础之响应式原理》的4.3章节中已经做过详细的分析,这里就不再赘述。
  • 跟着,调用get成员方法去进行依赖收集

下面我们将再次看下get成员方法的代码

get() {pushTarget(this);let value;const vm = this.vm;try {value = this.getter.call(vm, vm);} catch (e) {if (this.user) {handleError(e, vm, `getter for watcher "${this.expression}"`);} else {throw e;}} finally {// "touch" every property so they are all tracked as// dependencies for deep watchingif (this.deep) {traverse(value);}popTarget();this.cleanupDeps();}return value;}

这个方法我们此前的文章也分析过很多次了,目的就是调用getter,即读取下我们监控的响应式属性,在我们的示例中,就是读取一下vm中的count,从而触发其getter,进而将我们的用户watcher加入到该响应式属性的依赖deb.subs中,即完成依赖收集过程。

如此一来,在下次有人修改了该响应式数据之后,将会遍历该响应式数据的所有订阅者,即所有依赖的watcher,包含这里的用户watcher,然后通知这些watcher去做事情,或者是像计算属性watcher那样去更新数据,或者像渲染watcher那样去重新计算虚拟DOM然后更新页面,或者是我们这里的用户watcher,则会重新执行下我们自己编写的handler回调。

以上,就是用户watcher的源码的简单分析。而这一系列的三个不同用途的watcher的源码分析也就告一段落了。多谢大家的观看和支持吧。

我是@天地会珠海分舵,「青葱日历」和「三日清单」作者。能力一般,水平有限,觉得我说的还有那么点道理的不妨点个赞关注下!

Vue源码之用户watcher相关推荐

  1. Vue源码之渲染watcher

    1. 前文回顾 在上一篇文章<Vue源码之计算属性watcher>中,我们学习了计算属性watcher是如何与计算属性的computedGetter协作,在计算属性所依赖的数据发生变化时, ...

  2. Vue源码实现之watcher拾遗

    目录 1. Watcher构造函数参数options和渲染watcher标志位 2. watcher收集的新老依赖deps和newDeps的作用 3. watcher中getter的目的就是去touc ...

  3. Vue源码之计算属性watcher

    在之前的文章<Vue源码分析基础之响应式原理>和<Vue源码实现之watcher拾遗>中,我们学习了watcher的实现原理.紧跟着这几天准备花点时间学习下watcher在vu ...

  4. 0215前端日报:vue源码剖析思维导图

    给 「前端开发博客」 加星标,每天打卡学习 长按二维码即可识别"进入网页"查看哟~ 1.vue源码剖析思维导图(一) 趁这个"难得"的假期,学习了一下vue源码 ...

  5. Vue源码学习之Computed与Watcher原理

    前言  computed与watch是我们在vue中常用的操作,computed是一个惰性求值观察者,具有缓存性,只有当依赖发生变化,第一次访问computed属性,才会计算新的值.而watch则是当 ...

  6. vue源码分析系列二:$mount()和new Watcher()的执行过程

    续vue源码分析系列一:new Vue的初始化过程 在initMixin()里面调用了$mount() if (vm.$options.el) {vm.$mount(vm.$options.el);/ ...

  7. vue源码:Watcher系列(一)

    少年驰骋,仗剑天涯 愿你眼眸有星辰,心中有大海 从此,以梦为马,不负韶华 在分析之前我们先来看看,vue中都有哪些Watcher种类呢?以及分别在什么时候创建呢?从vue源码里面看,Watcher是一 ...

  8. 深入剖析Vue源码 - 响应式系统构建(上)

    从这一小节开始,正式进入Vue源码的核心,也是难点之一,响应式系统的构建.这一节将作为分析响应式构建过程源码的入门,主要分为两大块,第一块是针对响应式数据props,methods,data,comp ...

  9. vue源码-对于「计算属性」的理解

    vue源码-对于「计算属性」的理解 这是我最近学习vue源码的一个个人总结和理解,所以可能并不适合每一位读者 本文的整体脉络如下,首先尽可能去掉细节,对计算属性源码的大致实现有一个了解,然后举一例子, ...

最新文章

  1. Eclipse 代码风格配置
  2. boost::hana::make_tuple用法的测试程序
  3. Codeforces Round #547 (Div. 3)
  4. python 使用requests模块进行 视频文件的下载
  5. 在java中原始时间_Java 日期时间
  6. coupled/decoupled
  7. JS 动态清除Div中内容
  8. springboot如何使用多线程,线程池管理
  9. 【POJ 3276】【开关问题】Face The Right Way【暑期 No.4】
  10. MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)
  11. Axure RP 9下载安装
  12. 服务器部署dble全流程
  13. 【饭谈】职业生涯的关键:不破不立
  14. 网上图书商城网上书店系统(jsp+mysql)
  15. 10 Kafka集群与运维
  16. F. Equalize the Array【学习进度条2】
  17. 大数据学习——相关资源
  18. android TV端如何读取微信网页版二维码显示生成一张图片
  19. 此生,依旧是我未看破红尘。
  20. 对动量守恒定律的质疑

热门文章

  1. android 通过adb工具实现无线连接安卓设备并调试
  2. P2p网站建设解决方案
  3. HDU2099 整除的尾数
  4. 英语爱阅读--I hope you live a life you're proud of
  5. 这后台管理系统,有逼格!(附源码)
  6. js 判断两个时间相差多少月_js对日期操作 获取两个日期的相差是否在几月之内...
  7. Docker安装Elasticsearch及安装中文分词插件
  8. C++ STL 中大根堆,小根堆的应用。
  9. 什么是HashMap?
  10. ldp hello报文接收的处理流程