Vue原理解析之observer模块

  • vue.js
JoeRay61 2017年02月16日发布

本文是针对Vue@2.1.8进行分析

observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭observer的支持。

observer模块在Vue项目中的代码位置是src/core/observer,模块共分为这几个部分:

  • Observer: 数据的观察者,让数据对象的读写操作都处于自己的监管之下

  • Watcher: 数据的订阅者,数据的变化会通知到Watcher,然后由Watcher进行相应的操作,例如更新视图

  • DepObserverWatcher的纽带,当数据变化时,会被Observer观察到,然后由Dep通知到Watcher

示意图如下:

Observer

Observer类定义在src/core/observer/index.js中,先来看一下Observer的构造函数

constructor (value: any) {this.value = valuethis.dep = new Dep()this.vmCount = 0def(value, '__ob__', this)if (Array.isArray(value)) {const augment = hasProto? protoAugment: copyAugmentaugment(value, arrayMethods, arrayKeys)this.observeArray(value)} else {this.walk(value)}
}

value是需要被观察的数据对象,在构造函数中,会给value增加__ob__属性,作为数据已经被Observer观察的标志。如果value是数组,就使用observeArray遍历value,对value中每一个元素调用observe分别进行观察。如果value是对象,则使用walk遍历value上每个key,对每个key调用defineReactive来获得该key的set/get控制权。

解释下上面用到的几个函数的功能:

  • observeArray: 遍历数组,对数组的每个元素调用observe

  • observe: 检查对象上是否有__ob__属性,如果存在,则表明该对象已经处于Observer的观察中,如果不存在,则new Observer来观察对象(其实还有一些判断逻辑,为了便于理解就不赘述了)

  • walk: 遍历对象的每个key,对对象上每个key的数据调用defineReactive

  • defineReactive: 通过Object.defineProperty设置对象的key属性,使得能够捕获到该属性值的set/get动作。一般是由Watcher的实例对象进行get操作,此时Watcher的实例对象将被自动添加到Dep实例的依赖数组中,在外部操作触发了set时,将通过Dep实例的notify来通知所有依赖的watcher进行更新。

如果不太理解上面的文字描述可以看一下图:

Dep

DepObserverWatcher之间的纽带,也可以认为Dep是服务于Observer的订阅系统。Watcher订阅某个ObserverDep,当Observer观察的数据发生变化时,通过Dep通知各个已经订阅的Watcher

Dep提供了几个接口:

  • addSub: 接收的参数为Watcher实例,并把Watcher实例存入记录依赖的数组中

  • removeSub: 与addSub对应,作用是将Watcher实例从记录依赖的数组中移除

  • dependDep.target上存放这当前需要操作的Watcher实例,调用depend会调用该Watcher实例的addDep方法,addDep的功能可以看下面对Watcher的介绍

  • notify: 通知依赖数组中所有的watcher进行更新操作

Watcher

Watcher是用来订阅数据的变化的并执行相应操作(例如更新视图)的。Watcher的构造器函数定义如下:

constructor (vm, expOrFn, cb, options) {this.vm = vmvm._watchers.push(this)// optionsif (options) {this.deep = !!options.deepthis.user = !!options.userthis.lazy = !!options.lazythis.sync = !!options.sync} else {this.deep = this.user = this.lazy = this.sync = false}this.cb = cbthis.id = ++uid // uid for batchingthis.active = truethis.dirty = this.lazy // for lazy watchersthis.deps = []this.newDeps = []this.depIds = new Set()this.newDepIds = new Set()this.expression = process.env.NODE_ENV !== 'production'? expOrFn.toString(): ''if (typeof expOrFn === 'function') {this.getter = expOrFn} else {this.getter = parsePath(expOrFn)if (!this.getter) {this.getter = function () {}process.env.NODE_ENV !== 'production' && warn(`Failed watching path: "${expOrFn}" ` +'Watcher only accepts simple dot-delimited paths. ' +'For full control, use a function instead.',vm)}}this.value = this.lazy? undefined: this.get()
}

参数中,vm表示组件实例,expOrFn表示要订阅的数据字段(字符串表示,例如a.b.c)或是一个要执行的函数,cb表示watcher运行后的回调函数,options是选项对象,包含deepuserlazy等配置。

watcher实例上有这些方法:

  • get: 将Dep.target设置为当前watcher实例,在内部调用this.getter,如果此时某个被Observer观察的数据对象被取值了,那么当前watcher实例将会自动订阅数据对象的Dep实例

  • addDep: 接收参数dep(Dep实例),让当前watcher订阅dep

  • cleanupDeps: 清除newDepIdsnewDep上记录的对dep的订阅信息

  • update: 立刻运行watcher或者将watcher加入队列中等待统一flush

  • run: 运行watcher,调用this.get()求值,然后触发回调

  • evaluate: 调用this.get()求值

  • depend: 遍历this.deps,让当前watcher实例订阅所有dep

  • teardown: 去除当前watcher实例所有的订阅

Array methods

src/core/observer/array.js中,Vue框架对数组的pushpopshiftunshiftsortsplicereverse方法进行了改造,在调用数组的这些方法时,自动触发dep.notify(),解决了调用这些函数改变数组后无法触发更新的问题。在Vue的官方文档中对这个也有说明:http://cn.vuejs.org/v2/guide/list.html#变异方法

本文转自https://segmentfault.com/a/1190000008377887

Vue原理解析之observer模块相关推荐

  1. Vue 原理解析(五)之 虚拟Dom 到真实Dom的转换过程

    上一篇 vue 原理解析(四): 虚拟Dom 是怎么生成的 再有一颗树形结构的Javascript对象后, 我们需要做的就是讲这棵树跟真实Dom树形成映射关系.我们先回顾之前的mountComponn ...

  2. Vue响应式数据: Observer模块实现

    前言 首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励.接下来的日子我应该会着力写一系列关于Vue与React内部原理的文 ...

  3. Vue响应式数据: Observer模块实现 1

    前言   首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励.接下来的日子我应该会着力写一系列关于Vue与React内部原理 ...

  4. Vue原理解析:手写路由管理器 —— Vue-Router

    由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏.等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码 一.对外暴露的入口文件index.js import Vue from ...

  5. vue render函数_Vue原理解析(一):Vue到底是什么?

    Vue,现在前端的当红炸子鸡,随着热度指数上升,实在是有必要从源码的角度,对它功能的实现原理一窥究竟.个人觉得看源码主要是看两样东西,从宏观上来说是它的设计思想和实现原理:微观上来说就是编程技巧,也就 ...

  6. vue原理之observer

    上节课我们讲了vue五种类型中的vue 实现data响应 这节课我们讲第二中类型observer Observer 功能 负责把 data 选项中的属性转换成响应式数据 data 中的某个属性也是对象 ...

  7. element 往node里面增加属性值_【Vue原理】Compile - 源码版 之 Parse 属性解析

    写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本 [2.5.17] 如果你觉得排版难 ...

  8. 音乐、视频播放模式切换实现方案及原理解析(基于vue、vuex、h5 audio)

    音乐.视频播放模式切换实现方案及原理解析(基于vue.vuex.h5 audio) 播放模式有三种: 顺序播放 随机播放 单曲循环 定义为一个playMode对象并向外暴露,内含三种播放模式,即为: ...

  9. Vue的路由实现原理解析(最清晰)

    Vue的路由实现原理解析(最清晰) 一般源码中,都会用到 window.history 和 location.hash history 实现 window.history 对象包含浏览器的历史,win ...

最新文章

  1. Repeater 嵌套 绑定数据,嵌套的Repeater无法绑定的问题
  2. Python推荐算法讲解
  3. 解决:EXCEL复制粘贴,精度丢失
  4. 宏与内联(inline)的区别(转载)
  5. leetcode701. 二叉搜索树中的插入操作(dfs)
  6. php生成网页缩略图接口,php生成网站缩略图
  7. Windows安装.net Framework时安装不上,提示已处理证书链,但是在不受信任提供程序信任的根证书中终止
  8. 继淘宝特价版之后 闲鱼已向微信提交小程序申请
  9. 重庆首个地方区块链标准本月起实施
  10. 【Java从0到架构师】Spring - 生命周期、代理
  11. 用Eclipse制作博客
  12. 曲线拟合最小二乘法对数c语言实现,数值计算_第6章曲线拟合的最小二乘法
  13. Postfix+Dovecot搭建MailServer配置说明
  14. GIS数据转换成CAD数据,还原显示CASS码、符号样式及高程值等图形属性的解决方案,shp转dwg,arcgis数据转CAD数据
  15. 多个blockquote_换句话说:使用blockquote,cite和q元素
  16. 腾讯防水墙php接入,PHP实现腾讯防水墙验证码校验
  17. java 获取视频第一帧 | Java工具类
  18. linux超级管理员名字修改,linux添加超级管理员用户,修改,删除用户
  19. 2022年全球与中国环己胺市场现状及未来发展趋势
  20. python编写程序、实现一个数字金字塔_python实现输入任意一个大写字母生成金字塔的示例...

热门文章

  1. 执业药师考试难度大吗丨备考攻略
  2. Wiley:International Journal for Numerical Methods in Biomedical Engineering投稿流程
  3. php56 debuginfo,FluffOS 中文站 | debug_info() 函数
  4. angular12项目对IE浏览器的支持
  5. 3.11 怎么增加小红书评论区的互动?【玩赚小红书】
  6. A Brief Bloom Filter(英文标题唬人罢了)
  7. 问题事件名称: BEX
  8. ardupilot 位置控制(POSHOLD)分析
  9. [LeetCode-70]-Climbing Stairs(爬楼梯,斐波那契数列问题)
  10. python实现矩阵横竖斜的和相等_python打印9宫格,25宫格等奇数格,且横竖斜相加和相等...