Vue原理解析之observer模块
Vue原理解析之observer模块
- vue.js
本文是针对
Vue@2.1.8
进行分析
observer
是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭observer
的支持。
observer
模块在Vue项目中的代码位置是src/core/observer
,模块共分为这几个部分:
Observer
: 数据的观察者,让数据对象的读写操作都处于自己的监管之下Watcher
: 数据的订阅者,数据的变化会通知到Watcher
,然后由Watcher
进行相应的操作,例如更新视图Dep
:Observer
与Watcher
的纽带,当数据变化时,会被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
Dep
是Observer
与Watcher
之间的纽带,也可以认为Dep
是服务于Observer
的订阅系统。Watcher
订阅某个Observer
的Dep
,当Observer
观察的数据发生变化时,通过Dep
通知各个已经订阅的Watcher
。
Dep
提供了几个接口:
addSub
: 接收的参数为Watcher
实例,并把Watcher
实例存入记录依赖的数组中removeSub
: 与addSub
对应,作用是将Watcher
实例从记录依赖的数组中移除depend
:Dep.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
是选项对象,包含deep
、user
、lazy
等配置。
watcher
实例上有这些方法:
get
: 将Dep.target
设置为当前watcher
实例,在内部调用this.getter
,如果此时某个被Observer
观察的数据对象被取值了,那么当前watcher
实例将会自动订阅数据对象的Dep
实例addDep
: 接收参数dep
(Dep实例),让当前watcher
订阅dep
cleanupDeps
: 清除newDepIds
和newDep
上记录的对dep的订阅信息update
: 立刻运行watcher
或者将watcher
加入队列中等待统一flushrun
: 运行watcher
,调用this.get()
求值,然后触发回调evaluate
: 调用this.get()
求值depend
: 遍历this.deps
,让当前watcher
实例订阅所有dep
teardown
: 去除当前watcher
实例所有的订阅
Array methods
在src/core/observer/array.js
中,Vue框架对数组的push
、pop
、shift
、unshift
、sort
、splice
、reverse
方法进行了改造,在调用数组的这些方法时,自动触发dep.notify()
,解决了调用这些函数改变数组后无法触发更新的问题。在Vue的官方文档中对这个也有说明:http://cn.vuejs.org/v2/guide/list.html#变异方法
本文转自https://segmentfault.com/a/1190000008377887
Vue原理解析之observer模块相关推荐
- Vue 原理解析(五)之 虚拟Dom 到真实Dom的转换过程
上一篇 vue 原理解析(四): 虚拟Dom 是怎么生成的 再有一颗树形结构的Javascript对象后, 我们需要做的就是讲这棵树跟真实Dom树形成映射关系.我们先回顾之前的mountComponn ...
- Vue响应式数据: Observer模块实现
前言 首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励.接下来的日子我应该会着力写一系列关于Vue与React内部原理的文 ...
- Vue响应式数据: Observer模块实现 1
前言 首先欢迎大家关注我的Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励.接下来的日子我应该会着力写一系列关于Vue与React内部原理 ...
- Vue原理解析:手写路由管理器 —— Vue-Router
由于时间问题,暂时先把代码完整的贴上来,感兴趣的朋友可以自行研究或收藏.等我那时有时间的时候,对专栏文章进行排序,并逐一讲解代码 一.对外暴露的入口文件index.js import Vue from ...
- vue render函数_Vue原理解析(一):Vue到底是什么?
Vue,现在前端的当红炸子鸡,随着热度指数上升,实在是有必要从源码的角度,对它功能的实现原理一窥究竟.个人觉得看源码主要是看两样东西,从宏观上来说是它的设计思想和实现原理:微观上来说就是编程技巧,也就 ...
- vue原理之observer
上节课我们讲了vue五种类型中的vue 实现data响应 这节课我们讲第二中类型observer Observer 功能 负责把 data 选项中的属性转换成响应式数据 data 中的某个属性也是对象 ...
- element 往node里面增加属性值_【Vue原理】Compile - 源码版 之 Parse 属性解析
写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧 研究基于 Vue版本 [2.5.17] 如果你觉得排版难 ...
- 音乐、视频播放模式切换实现方案及原理解析(基于vue、vuex、h5 audio)
音乐.视频播放模式切换实现方案及原理解析(基于vue.vuex.h5 audio) 播放模式有三种: 顺序播放 随机播放 单曲循环 定义为一个playMode对象并向外暴露,内含三种播放模式,即为: ...
- Vue的路由实现原理解析(最清晰)
Vue的路由实现原理解析(最清晰) 一般源码中,都会用到 window.history 和 location.hash history 实现 window.history 对象包含浏览器的历史,win ...
最新文章
- Repeater 嵌套 绑定数据,嵌套的Repeater无法绑定的问题
- Python推荐算法讲解
- 解决:EXCEL复制粘贴,精度丢失
- 宏与内联(inline)的区别(转载)
- leetcode701. 二叉搜索树中的插入操作(dfs)
- php生成网页缩略图接口,php生成网站缩略图
- Windows安装.net Framework时安装不上,提示已处理证书链,但是在不受信任提供程序信任的根证书中终止
- 继淘宝特价版之后 闲鱼已向微信提交小程序申请
- 重庆首个地方区块链标准本月起实施
- 【Java从0到架构师】Spring - 生命周期、代理
- 用Eclipse制作博客
- 曲线拟合最小二乘法对数c语言实现,数值计算_第6章曲线拟合的最小二乘法
- Postfix+Dovecot搭建MailServer配置说明
- GIS数据转换成CAD数据,还原显示CASS码、符号样式及高程值等图形属性的解决方案,shp转dwg,arcgis数据转CAD数据
- 多个blockquote_换句话说:使用blockquote,cite和q元素
- 腾讯防水墙php接入,PHP实现腾讯防水墙验证码校验
- java 获取视频第一帧 | Java工具类
- linux超级管理员名字修改,linux添加超级管理员用户,修改,删除用户
- 2022年全球与中国环己胺市场现状及未来发展趋势
- python编写程序、实现一个数字金字塔_python实现输入任意一个大写字母生成金字塔的示例...
热门文章
- 执业药师考试难度大吗丨备考攻略
- Wiley:International Journal for Numerical Methods in Biomedical Engineering投稿流程
- php56 debuginfo,FluffOS 中文站 | debug_info() 函数
- angular12项目对IE浏览器的支持
- 3.11 怎么增加小红书评论区的互动?【玩赚小红书】
- A Brief Bloom Filter(英文标题唬人罢了)
- 问题事件名称: BEX
- ardupilot 位置控制(POSHOLD)分析
- [LeetCode-70]-Climbing Stairs(爬楼梯,斐波那契数列问题)
- python实现矩阵横竖斜的和相等_python打印9宫格,25宫格等奇数格,且横竖斜相加和相等...