梳理vue双向绑定的实现原理
Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。
要实现mvvm的双向绑定,就必须要实现以下几点:
Compile—指令解析系统,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
Observer—数据监听系统,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
Dep+Watcher—发布订阅模型,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图。
Dep是发布订阅者模型中的发布者:get数据的时候,收集订阅者,触发Watcher的依赖收集;set数据时发布更新,通知Watcher 。一个Dep实例对应一个对象属性或一个被观察的对象,用来收集订阅者和在数据改变时,发布更新。
Watcher是发布订阅者模型中的订阅者:订阅的数据改变时执行相应的回调函数(更新视图或表达式的值)。一个Watcher可以更新视图,如html模板中用到的{{test}},也可以执行一个$watch监督的表达式的回调函数(Vue实例中的watch项底层是调用的$watch实现的),还可以更新一个计算属性(即Vue实例中的computed项)。
mvvm入口函数,整合以上三者,具体如图所示:

compire可以参看《双向绑定的实现原理》,这里不做过多解读。
Observer,Dep和Watcher类的实现及原理,推荐阅读《Vue源码解读一:Vue数据响应式原理》,一般开发者需要关注:
收集依赖指的是谁收集依赖,依赖又是指的什么?
Watcher,作用是分割表达式,收集依赖并且在值变化的时候调用回调函数。
我们上面说过一个Dep对应着一个数据(这个数据可能是:对象的属性、一个对象、一个数组);一个Watcher对应可以是一个模板也可以是一个$watch对应的表达式、函数等,无论那种情况,他们都依赖于data里面的数据,所以这里说的依赖其实就是模板或表达式所依赖的数据,对应着相关数据的Dep。
Watcher的四个使用场景
第一种:观察模板中的数据
第二种:观察创建Vue实例时watch选项里的数据
第三种:观察创建Vue实例时computed选项里的数据所依赖的数据
第四种:调用$watch api观察的数据或表达式
Watcher只有在这四种场景中,Watcher才会收集依赖,更新模板或表达式,否则,数据改变后,无法通知依赖这个数据的模板或表达式:
所以在解决数据改变,模板或表达式没有改变的问题时,可以这么做:
首先仔细看一看数据是否在上述四种应用场景中,以便确认数据已经收集依赖;其次查看改变数据的方式,确定这种方式会使数据的改变被拦截(关于这一点,上面Obsever相关内容中说的比较多)。
对于Observer需要注意的是:

getter/setter方法拦截数据的不足
当对象增删的时候,是监控不到的。比如:data={a:"a"},这个时候如果我们设置data.test="test",这个时候是监控不到的。因为在observe data的时候,会遍历已有的每个属性(比如a),添加getter/setter,而后面设置的test属性并没有机会设置getter/setter,所以检测不到变化。同样的,删除对象属性的时候,getter/setter会跟着属性一起被删除掉,拦截不到变化。
vm.$set/Vue.set和vm.$delete/Vue.delete这样的api来解决这个问题
getter/setter是针对对象的对于数组的修改(push(),pop(),shift(),unshift(),splice(),sort(),reverse())等方法,arr发生了改变,此时是需要更新视图的,但是arr的getter/setter拦截不到变化(只有在赋值的时候才会调用setter,比如:arr=[6,7,8])。
对于这种情况,vue通过改写Array的默认方法,在调用这些方法的时候发布更新消息。一般无需关注,但是对于如下两种情况:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
需要vm.$set/Vue.set和vm.items.splice(newLength)解决,具体参看官方说明
每次给数据设置值得时候,都会调用setter函数,这个时候就会发布属性更新消息,即使数据的值没有变。从性能方便考虑我们肯定希望值没有变化的时候,不更新模板。(像Angular这样把批量操作延时到一次更新,一次做完所有数据变更,然后整体应用到界面上)

整体感知virtual DOM
virtual DOM分为三个步骤:
1.createElement(): 用 JavaScript对象(虚拟树) 描述 真实DOM对象(真实树)
2.diff(oldNode, newNode) : 对比新旧两个虚拟树的区别,收集差异
3.patch() : 将差异应用到真实DOM树
有的时候 第二步 可能与 第三步 合并成一步(Vue 中的patch就是这样)
Vue的实现原理总结
首先,在实例化的过程中,把一个普通 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。
Dep 是一个依赖收集器。data 下的每一个属性都有一个唯一的 Dep 对象,在 get 中收集仅针对该属性的依赖,然后在 set 方法中触发所有收集的依赖。
在Watcher中对表达式求值,从而触发数据的get。在求值之前将当前Watch实例设置到全局,使用pushTarget(this)方法。
在get()中收集依赖,this.subs.push(sub),set的时候触发回调Dep.notify()。
Compile中首先将template或el编译成render函数,render函数返回一个虚拟DOM对象(将模板转为 render 函数的时候,实际是先生成的抽象语法树(AST),再将抽象语法树转成的 render 函数)
当 vm._render 执行的时候,所依赖的变量就会被求值,并被收集为依赖。按照Vue中 watcher.js 的逻辑,当依赖的变量有变化时不仅仅回调函数被执行,实际上还要重新求值,即还要执行一遍
如果还没有 prevVnode 说明是首次渲染,直接创建真实DOM。如果已经有了 prevVnode 说明不是首次渲染,那么就采用 patch 算法进行必要的DOM操作。这就是Vue更新DOM的逻辑。
最后,安利下:《Vue.js 技术揭秘》
参考文章
文自《梳理vue双向绑定的实现原理 - vue入坑总结 - 周陆军的个人网站》,如有不妥之前,请源站留言告知。
梳理vue双向绑定的实现原理的更多相关文章
- Vue双向绑定的实现原理系列(四):补充指令解析器compile
补充指令解析器compile github源码 补充下HTML节点类型的知识: 元素节点 Node.ELEMENT_NODE(1) 属性节点 Node.ATTRIBUTE_NODE(2) 文本节点 N ...
- Vue双向绑定的实现原理系列(一):Object.defineproperty
了解Object.defineProperty() github源码 Object.defineProperty()方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象. ...
- Vue双向绑定的实现原理系列(三):监听器Observer和订阅者Watcher
监听器Observer和订阅者Watcher 实现简单版Vue的过程,主要实现{{}}.v-model和事件指令的功能 主要分为三个部分 github源码 1.数据监听器Observer,能够对数据对 ...
- Vue双向绑定的实现原理及简单实现
vue数据双向绑定原理 vue数据双向绑定是通过(数据劫持)+(发布者-订阅者模式)的方式来实现的,而所谓的数据劫持就是通过Object.defineProperty() 来实现的,所谓的Obje ...
- vue双向绑定原理分析
当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/jiangzhenf ...
- [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅
有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...
- Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)
Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...
- vue双向绑定的原理及实现双向绑定MVVM源码分析
vue双向绑定的原理及实现双向绑定MVVM源码分析 双向数据绑定的原理是:可以将对象的属性绑定到UI,具体的说,我们有一个对象,该对象有一个name属性,当我们给这个对象name属性赋新值的时候,新值 ...
- Vue双向绑定原理,教你一步一步实现双向绑定
当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...
随机推荐
- 大数据小白系列 —— MapReduce流程的深入说明
上一期我们介绍了MR的基本流程与概念,本期稍微深入了解一下这个流程,尤其是比较重要但相对较少被提及的Shuffling过程. Mapping 上期我们说过,每一个mapper进程接收并处理一块数据,这 ...
- python 爬虫与数据可视化--数据提取与存储
一.爬虫的定义.爬虫的分类(通用爬虫.聚焦爬虫).爬虫应用场景.爬虫工作原理(最后会发一个完整爬虫代码) 二.http.https的介绍.url的形式.请求方法.响应状态码 url的形式: 请求头: ...
- 开源搜索引擎solr elasticsearch学习计划
其实不单单是研究solr elasticsearch把,进行调研性技术学习时,应该制定一些目标以及里程碑.新的技术调研 学习是一件很爽的事,能学到新技术新东西.但是在学习新技术同时,有几个问题是需要我 ...
- Java Web 浏览器关闭后Session就会被销毁吗?
浏览器关闭后Session就会被销毁吗? Session是JSP的九大内置对象(也称为隐含对象)中的一个,用于保存当前用户的状态信息,初学者可能认为Session的生命周期是从打开一个浏览器发送请求到 ...
- [LeetCode] Design HashMap 设计HashMap
Design a HashMap without using any built-in hash table libraries. To be specific, your design should ...
- Solve Error: node postinstall sh: node: command not found
When install the yeoman using the following command: npm install -g yo You might have the following ...
- 如何将WORD表格转换成EXCEL表格
WORD和EXCEL都可以制作表格,但WORD表格与EXCEL表格之间有着很明显的差距,所以在办公中经常会需要将WORD转换成EXCEL,今天小编就教大家一招将WORD表格转换成EXCEL表格. 操作 ...
- 泡泡一分钟: Deep-LK for Efficient Adaptive Object Tracking
Deep-LK for Efficient Adaptive Object Tracking "链接:https://pan.baidu.com/s/1Hn-CVgiR7WV0jvaYBv5 ...
- 报文分析4、TCP协议的头结构
TCP协议的头结构 来源端口(2字节) 目的端口(2字节) 序号(4字节) 确认序号(4字节) 头长度(4位) 保留(6位) URG ACK PSH RST SYN PIN 窗口大小(2字节) 校验和 ...
- Angular2 ng2-smart-table
ng2-smart-table 入门 安装 你要做的就是运行以下命令: npm install --save ng2-smart-table 此命令将创建在你的`package.json`文件和安装包 ...