Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后进行相应的处理。

要实现mvvm的双向绑定,就必须要实现以下几点:

  1. Compile—指令解析系统,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数

  2. Observer—数据监听系统,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者

  3. 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方法拦截数据的不足

  1. 当对象增删的时候,是监控不到的。比如: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来解决这个问题

  2. getter/setter是针对对象的对于数组的修改(push(),pop(),shift(),unshift(),splice(),sort(),reverse())等方法,arr发生了改变,此时是需要更新视图的,但是arr的getter/setter拦截不到变化(只有在赋值的时候才会调用setter,比如:arr=[6,7,8])。

    对于这种情况,vue通过改写Array的默认方法,在调用这些方法的时候发布更新消息。一般无需关注,但是对于如下两种情况:

    1. 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue

    2. 当你修改数组的长度时,例如: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的实现原理总结

  1. 首先,在实例化的过程中,把一个普通 JavaScript 对象传给 Vue 实例的 data选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。

  2. Dep 是一个依赖收集器。data 下的每一个属性都有一个唯一的 Dep 对象,在 get 中收集仅针对该属性的依赖,然后在 set 方法中触发所有收集的依赖。

  3. 在Watcher中对表达式求值,从而触发数据的get。在求值之前将当前Watch实例设置到全局,使用pushTarget(this)方法。

  4. 在get()中收集依赖,this.subs.push(sub),set的时候触发回调Dep.notify()。

  5. Compile中首先将template或el编译成render函数,render函数返回一个虚拟DOM对象(将模板转为 render 函数的时候,实际是先生成的抽象语法树(AST),再将抽象语法树转成的 render 函数)

  6. 当 vm._render 执行的时候,所依赖的变量就会被求值,并被收集为依赖。按照Vue中 watcher.js 的逻辑,当依赖的变量有变化时不仅仅回调函数被执行,实际上还要重新求值,即还要执行一遍

  7. 如果还没有 prevVnode 说明是首次渲染,直接创建真实DOM。如果已经有了 prevVnode 说明不是首次渲染,那么就采用 patch 算法进行必要的DOM操作。这就是Vue更新DOM的逻辑。

最后,安利下:《Vue.js 技术揭秘》

参考文章

梳理Vue2.0双向绑定的实现原理

文自《梳理vue双向绑定的实现原理 - vue入坑总结 - 周陆军的个人网站》,如有不妥之前,请源站留言告知。

转载于:https://juejin.im/post/5c6552d65188252b194e8d75

梳理vue双向绑定的实现原理相关推荐

  1. 记一次对vue双向绑定的理解

    之前有看过一次vue双向绑定原理实现相关的博客,看得似懂非懂的,然后也就搁浅了. 昨天脑海里又突然燃起了要不这块搞懂的冲动,于是乎又开始了一轮博客轰炸,综合研究了多位大神写得关于vue双向绑定的实现原 ...

  2. vue 双向绑定 getter 和 setter

    它的每个属性都有两个相对应的get和set方法,我觉的这是多此一举的,于是去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理完全不同,Angular是用的数据脏检测 ...

  3. 前端技巧|vue双向绑定原理,助你面试成功

    在面试一些大厂的时候,面试官可能会问到你vue双向数据绑定的原理是什么?有些小伙伴不知道是什么东西,这样你在面试官的眼里就大打折扣了.今天小千就来给大家介绍一下vue的双向绑定原理,千万不要错过啦. ...

  4. Vue双向绑定:原理篇(详细)

    文章目录 前言 什么是响应式 数据劫持 发布者-订阅者模式 模式简介 发布者 Observer 订阅器 dep 订阅者 Watcher 整体流程 初始化data data变为响应式数据 解析模板 收集 ...

  5. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...

  6. 【vue双向绑定原理浅析】

    vue双向绑定原理浅析 1.什么是双向绑定? ​ 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据.(数据变化更新视图,视图变 ...

  7. vue双向绑定的原理

    之前我有个学生在面试的时候,面试官问vue的双向绑定如何实现?学生说用v-module实现.又问那么双向绑定的原理是什么?就回答不上来了,这个offer工资在18k左右,其他问题都回答上来了,如果这个 ...

  8. VUE双向绑定的原理(简单版)+虚拟DOM 节点的创建和更新

    手动敲敲代码,就很容易理解了,供参考 1.以下是VUE双向绑定的原理(简单版) 主要是监听和defineProperty实现简单的双向绑定 <html><head></h ...

  9. Vue双向绑定原理代码实现

    1.代码实现Vue双向绑定与事件绑定,v-bind v-model v-on DOM结构准备 <body><div id="app"><form> ...

最新文章

  1. 难搞的偏向锁终于要被Java移除了
  2. Spring Aop实例之xml配置
  3. BZOJ2705 [SDOI2012]Longge的问题 欧拉函数
  4. Opportunity PRODUCT GUID
  5. 自定义注解 实现自定义消息_实现自定义的未来
  6. 深度学习pytorch--softmax回归(一)
  7. qq ip探测仪 php,巧用Win7资源监视器,查看QQ好友IP
  8. 微软正式提供免费杀毒软件下载 仅限7.5万份
  9. 共享文件夹只能连接20人_英语正能量 | 快乐可以与人共享,苦难却只能自己坚强...
  10. 用Map集合来统计一个字符串数组中每个字符串的个数
  11. FMEA软件之旧版FMEA导入及快速转换为新版FMEA(FMEAHunter)
  12. 【Python】SyntaxError: invalid syntax jedi
  13. 直播软件搭建时如何在视频通话中加入美颜处理
  14. Java复习:确定给定日期是一年的第几天
  15. 攻防世界 pwn进阶区----No.012 babyfengshui 解题思路
  16. eclipse中包里建包
  17. java 日食还是月蚀,2018年日食和月食时间
  18. 公司常用的Project管理工具
  19. VMware虚拟机ubuntu显示屏幕太小解决问题
  20. Python爬虫-面向对象-《传闻中的陈芊芊》豆瓣热评

热门文章

  1. 【目标检测基础积累】常用的评价指标
  2. 旷视 IPO 在即,看清“AI 第一股”的商业真相
  3. 从α到μ:DeepMind棋盘游戏AI进化史
  4. 2019年上半年收集到的人工智能迁移学习干货文章
  5. 专家认为自动驾驶汽车需要很多年的五个原因
  6. 讨论Markov Random Field和Gibbs Random Field心得
  7. python如何去掉一个字符串两边的引号
  8. 浙江发布数字化改革标准化体系建设方案,将于2025年底建成
  9. 震撼三观:从细胞到宇宙
  10. 中国芯片将靠此超车!RISC-V架构神在哪全解构