阅读更多

0顶
0踩

企业架构
引用
【前言】本文将帮助大家了解Vue的双向数据绑定原理、核心代码模块,以及如何实现双向绑定。为了便于说明原理与实现,本文相关代码主要摘自Vue源码, 并进行了简化改造,相对较简陋,并未考虑到数组的处理、数据的循环依赖等,也难免存在一些问题,欢迎大家指正。不过这些并不会影响大家的阅读和理解,相信看完本文后对大家在阅读Vue源码的时候会更有帮助。

所有相关代码均详见GitHub:https://github.com/DMQ/mvvm


相信大家对MVVM双向绑定应该都不陌生了,一言不合上代码,下面先看一个本文最终实现的效果吧,和Vue一样的语法,如果还不了解双向绑定,猛戳 Google


效果:


几种实现双向绑定的做法
目前几种主流的MVC(VM)框架都实现了单向数据绑定,而我所理解的双向数据绑定无非就是在单向绑定的基础上给可输入元素(input、textare等)添加了change(input)事件,来动态修改model和view,并没有多高深。所以无需太过介怀是实现的单向或双向绑定。

实现数据绑定的做法有大致如下几种:
  • 发布者-订阅者模式(backbone.js)
  • 脏值检查(angular.js)
  • 数据劫持(vue.js)
发布者-订阅者模式: 一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是vm.set('property', value),这里有篇文章讲的比较详细,有兴趣可点击查看

但我们更希望通过vm.property = value这种方式更新数据,同时自动更新视图,于是有了下面两种方式:

脏值检查:Angular.js是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过setInterval()定时轮询检测数据变动,当然Google不会这么low,Angular只有在指定的事件触发时进入脏值检测,大致如下:
  • DOM事件,譬如用户输入文本,点击按钮等。(ng-click)
  • XHR响应事件 ($http)
  • 浏览器Location变更事件 ($location)
  • Timer事件($timeout , $interval)
  • 执行$digest()或$apply()
数据劫持:Vue.js则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

思路整理
已经了解到vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目的,无疑这个方法是本文中最重要、最基础的内容之一,如果不熟悉defineProperty,可以点击此处查看。

整理了一下,要实现MVVM的双向绑定,就必须要实现以下几点:
  • 实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
  • 实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
  • 实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
  • MVVM入口函数,整合以上三者

上述流程如图所示:


1、实现Observer
ok, 思路已经整理完毕,也已经比较明确相关逻辑和模块功能了,let’s do it!

我们知道可以利用Obeject.defineProperty()来监听属性变动
那么将需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据变化。相关代码可以是这样:

这样我们已经可以监听每个数据的变化了,那么监听到变化之后就是怎么通知订阅者了,所以接下来我们需要实现一个消息订阅器,很简单,维护一个数组,用来收集订阅者,数据变动触发notify,再调用订阅者的update方法,代码改善之后是这样:

那么问题来了,谁是订阅者?怎么往订阅器添加订阅者?

没错,上面的思路整理中我们已经明确订阅者应该是Watcher,而且var dep = new Dep();是在defineReactive方法内部定义的,所以想通过dep添加订阅者,就必须要在闭包内操作,所以我们可以在getter里面动手脚:

这里已经实现了一个Observer了,已经具备了监听数据和数据变化通知订阅者的功能。那么接下来就是实现Compile了。

2、实现Compile
Compile主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,如图所示:

因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中:

compileElement方法将遍历所有节点及其子节点,进行扫描解析编译,调用对应的指令渲染函数进行数据渲染,并调用对应的指令更新函数进行绑定,详看代码及注释说明:


这里通过递归遍历保证了每个节点及子节点都会解析编译到,包括了{{}}表达式声明的文本节点。指令的声明规定是通过特定前缀的节点属性来标记,如<span v-text="content" other-attr中v-text便是指令,而other-attr不是指令,只是普通的属性。

监听数据、绑定更新函数的处理是在compileUtil.bind()这个方法中,通过new Watcher()添加回调来接收数据变化的通知。

至此,一个简单的Compile就完成了。接下来要看看Watcher这个订阅者的具体实现了。

3、实现Watcher
Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:
  • 在自身实例化时往属性订阅器(dep)里面添加自己;
  • 自身必须有一个update()方法;
  • 待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
如果有点乱,可以回顾下前面的思路整理:

实例化Watcher的时候,调用get()方法,通过Dep.target = watcherInstance标记订阅者是当前Watcher实例,强行触发属性定义的getter方法,getter方法执行的时候,就会在属性的订阅器dep添加当前Watcher实例,从而在属性值有变化的时候,watcherInstance就能收到更新通知。

Ok,Watcher也已经实现了。

基本上Vue中数据绑定相关比较核心的几个模块也是这几个,点击此处,在src目录可找到Vue源码。最后来讲讲MVVM入口文件的相关逻辑和实现吧,相对就比较简单了。

4、实现MVVM
MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

一个简单的MVVM构造器是这样子:

但是这里有个问题,从代码中可看出监听的数据对象是options.data,每次需要更新视图,则必须通过var vm = new MVVM({data:{name: 'kindeng'}}); vm._data.name = 'dmq';这样的方式来改变数据。

显然不符合我们一开始的期望,我们所期望的调用方式应该是这样的:var vm = new MVVM({data: {name: 'kindeng'}}); vm.name = 'dmq';

所以这里需要给MVVM实例添加一个属性代理的方法,使访问VM的属性代理为访问vm._data的属性,改造后的代码如下:

这里主要还是利用了Object.defineProperty()这个方法来劫持了VM实例对象的属性的读写权,使读写VM实例的属性转成读写了vm._data的属性值,达到鱼目混珠的效果。

至此,全部模块和功能已经完成了,如本文开头所承诺的两点。一个简单的MVVM模块已经实现,其思想和原理大部分来自经过简化改造的Vue源码,在这里可以看到本文的所有相关代码。

由于本文内容偏实践,所以代码量较多,且不宜列出大篇幅代码,所以建议想深入了解的童鞋可以再次结合本文源代码来进行阅读,这样会更加容易理解和掌握。

总结
本文主要围绕“几种实现双向绑定的做法”、“实现Observer”、“实现Compile”、“实现Watcher”、“实现MVVM”这几个模块来阐述双向绑定的原理和实现。并根据思路流程渐进梳理讲解了一些细节思路和比较关键的内容点,以及通过展示部分关键代码讲述怎样一步步实现一个双向绑定MVVM。文中可能会有一些不够严谨的思考和错误,欢迎大家指正,有兴趣欢迎一起探讨和改进。

引用
声明:本文来自腾讯增值产品部官方公众号小时光茶社,为CSDN原创投稿,未经许可,禁止任何形式的转载。
作者:邓木琴,腾讯前端工程师,手Q阅读前端开发,参与过手Q阅读、企鹅电竞、原创社区等项目。热衷于新技术研究,处女座,典型强迫症患者,注重细节。
责编:钱曙光,关注架构和算法领域,寻求报道或者投稿请发邮件qianshg@csdn.net,另有「CSDN 高级架构师群」,内有诸多知名互联网公司的大牛架构师,欢迎架构师加微信qianshuguangarch申请入群,备注姓名+公司+职位。
  • 大小: 35.8 KB
  • 大小: 8.5 KB
  • 大小: 29.8 KB
  • 大小: 45.4 KB
  • 大小: 37.3 KB
  • 大小: 28.4 KB
  • 大小: 15.7 KB
  • 大小: 36.1 KB
  • 大小: 71.1 KB
  • 大小: 37.8 KB
  • 大小: 68.2 KB
  • 大小: 13.2 KB
  • 大小: 44.1 KB
0
0
评论 共 1 条 请登录后发表评论
1 楼 semmy 2017-01-09 10:05

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • Vue原理剖析 实现双向绑定MVVM

    主要为大家剖析了Vue原理,实现双向绑定MVVM,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  • 81192!请返航!

    今天的文字就这么多,明白的,不明白的,就这些吧。

  • 现实版的黑客大战,这可能是中国黑客做过最燃的事情了

    由一场撞击事件引发的黑客大战,80000人参与、被攻击网站超4000多个……这场看似没有硝烟的战争当时是何情景?让我们梦回2001,一起看看吧。 “81192无法返航” ——“呼叫81192,这里是553,我奉命接替你机执行巡航任务,请返航!” ——“81192收到,我已无法返航,你们继续前进,...

  • vue实现数据双向绑定MVVM原理

    vue实现数据双向绑定主要是: 采用 数据劫持 结合 发布者-订阅者模式 的方式,数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变 核心( Vue2.0 ):通过Object.defineProperty()来劫持...

  • 基于C语言+AT89C52单片机搭建的PID直流电机控制程序,用于Proteus电路仿真+源码+开发文档(高分优秀项目)

    基于C语言+AT89C52单片机搭建的PID直流电机控制程序,用于Proteus电路仿真+源码+开发文档,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用~ 项目简介: 本文所介绍的基于AT89C52单片机的PID直流电机控制系统设计过程主要分为MATLAB建模、Proteus硬件电路的设计以及基于C51语法的单片机程序开发编写。本系统性质为单闭环控制,即通过转速对电机进行调速;将采用增量式编码器进行电机转速的测量,并设定PWM波形生成的开关频率为7KHz,速度回路的采样周期为14ms。 1. 所需仿真平台 单片机部分代码主要利用Keil uVision5平台进行编译,工具链采用C51。仿真电路搭建平台为Proteus 8 Professional。 2. Release版本 编译后的运行环境为Atmel AT89C52单片机平台。 3. 运行 将已发布的HEX文件放入Proteus 8 Professional仿真平台或以上版本内运行即可; 可通过修改Proteus仿真文件方波的频率(开关频率)来改变PWM相应的频率;

  • 基于.net8的物料管理系统

    {FB0886BF-35E0-480F-8B15-E4D52B351628}

  • Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar

    Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar Java开发案例-springboot-32-整合sharding-jdbc-源代码+文档.rar

  • 微信小程序-模仿知乎.rar

    微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎.rar微信小程序-模仿知乎

  • 基于FreeRTOS+STM32CubeMX+LCD1602+MCP6S28的8通道模拟可编程增益放大器Proteus仿真

    基于FreeRTOS+STM32CubeMX+LCD1602+MCP6S28的8通道模拟可编程增益放大器Proteus仿真

  • srs5.0 r2版本,gb28181流媒体服务软件

    srs5.0 r2版本,gb28181流媒体服务软件

  • 基于java的教务系统小程序源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

  • 基于Vue和Java的LBS客户流量分析系统设计源码

    LBS客户流量分析系统项目源码,共191个文件,采用Vue、JavaScript、CSS和HTML等语言开发,涉及多种文件类型如Vue组件、PNG图片、JSON配置文件、JavaScript脚本、JPG图片、CSS样式表、BABEL配置文件、EDITORCONFIG文件、ESLINTIGNORE文件和GIT忽略文件等。该项目是一个基于Vue和Java的LBS客户流量分析系统设计,旨在提供一个高效、稳定的新客流产品解决方案。

  • grpcio-1.34.1-cp36-cp36m-manylinux2014_i686.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

  • 对人脸图像进行性别和年龄的判断

    对人脸图像进行性别和年龄的判断

  • 第一篇20240528_04.png

    第一篇20240528_04.png

  • 基于Java的图书馆借阅管理系统源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

Global site tag (gtag.js) - Google Analytics