深入理解vue.js双向绑定的实现原理


Posted in Javascript onDecember 05, 2016

前言

大家都知道Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统。本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的。先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例。

一、访问器属性

访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过defineProperty()方法单独定义。

var obj = { };

// 为obj定义一个名为hello的访问器属性

Object.defineProperty(obj, "hello", {

get: function () {return sth},

set: function (val) {/* do sth */}

})

obj.hello // 可以像普通属性一样读取访问器属性

访问器属性的"值"比较特殊,读取或设置访问器属性的值,实际上是调用其内部特性:getset函数。

obj.hello // 读取属性,就是调用get函数并返回get函数的返回值

obj.hello = "abc" // 为属性赋值,就是调用set函数,赋值其实是传参深入理解vue.js双向绑定的实现原理

getset方法内部的this都指向obj,这意味着getset函数可以操作对象内部的值。另外,访问器属性的会"覆盖"同名的普通属性,因为访问器属性会被优先访问,与其同名的普通属性则会被忽略(也就是所谓的被"劫持"了)。

二、极简双向绑定的实现

深入理解vue.js双向绑定的实现原理

此例实现的效果是:随文本框输入文字的变化,span中会同步显示相同的文字内容;在js或控制台显式的修改obj.name的值,视图会相应更新。这样就实现了model =>view以及view => model的双向绑定,并且是响应式的。

深入理解vue.js双向绑定的实现原理

以上就是Vue实现双向绑定的基本原理。

三、分解任务

上述示例仅仅是为了说明原理。

我们最终要实现的是:

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

首先将该任务分成几个子任务:

1、输入框以及文本节点与data中的数据绑定

2、输入框内容变化时,data中的数据同步变化。即view => model的变化。

3、data中的数据变化时,文本节点的内容同步变化。即model => view的变化。

要实现任务一,需要对DOM进行编译,这里有一个知识点:DocumentFragment。

四、DocumentFragment

DocumentFragment(文档片段)可以看作节点容器,它可以包含多个子节点,当我们将它插入到DOM中时,只有它的子节点会插入目标节点,所以把它看作一组节点的容器。使用DocumentFragment处理节点,速度和性能远远优于直接操作DOM。Vue进行编译时,就是将挂载目标的所有子节点劫持(真的是劫持)到DocumentFragment中,经过一番处理后,再将DocumentFragment整体返回插入挂载目标。

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

五、数据初始化绑定

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

以上代码实现了任务一,我们可以看到,hello world已经呈现在输入框和文本节点中。

深入理解vue.js双向绑定的实现原理

六、响应式的数据绑定

再来看任务二的实现思路:当我们在输入框输入数据的时候,首先触发input事件(或者keyupchange事件),在相应的事件处理程序中,我们获取输入框的value并赋值给vm实例的text属性。我们会利用defineProperty将data中的text劫持为vm的访问器属性,因此给vm.text赋值,就会触发set方法。

set方法中主要做两件事,第一是更新属性的值,第二留到任务三再说。

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

任务二也就完成了,text属性值会与输入框的内容同步变化:

深入理解vue.js双向绑定的实现原理

七、订阅/发布模式(subscribe&publish)

text属性变化了,set方法触发了,但是文本节点的内容没有变化。如何让同样绑定到text的文本节点也同步变化呢?这里又有一个知识点:订阅发布模式。

订阅发布模式(又称观察者模式)定义了一种一对多的关系,让多个观察者同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察者对象。

发布者发出通知 => 主题对象收到通知并推送给订阅者 => 订阅者执行相应操作

深入理解vue.js双向绑定的实现原理

之前提到的,当set方法触发后做的第二件事就是作为发布者发出通知:“我是属性text,我变了”。文本节点则是作为订阅者,在收到消息后执行相应的更新操作。

八、双向绑定的实现

回顾一下,每当new一个Vue,主要做了两件事:第一个是监听数据:observe(data) ,第二个是编译HTML:nodeToFragement(id)

在监听数据的过程中,会为data中的每一个属性生成一个主题对象dep。

在编译HTML的过程中,会为每个与数据绑定相关的节点生成一个订阅者watcher,watcher会将自己添加到相应属性的dep中。

我们已经实现:修改输入框内容 => 在事件回调函数中修改属性值 => 触发属性的set方法。

接下来我们要实现的是:发出通知dep.notify() => 触发订阅者的update方法 => 更新视图。

这里的关键逻辑是:如何将watcher添加到关联属性的dep中。

深入理解vue.js双向绑定的实现原理

在编译HTML过程中,为每个与data关联的节点生成一个Watcher。Watcher函数中发生了什么呢?

深入理解vue.js双向绑定的实现原理

首先,将自己赋给了一个全局变量Dep.target

其次,执行了update方法,进而执行了get方法,get的方法读取了vm的访问器属性,从而触发了访问器属性的get方法,get方法中将该watcher添加到了对应访问器属性的dep中;

再次,获取属性的值,然后更新视图。

最后,将Dep.target设为空。因为它是全局变量,也是watcher与dep关联的唯一桥梁,任何时刻都必须保证Dep.target只有一个值。

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

深入理解vue.js双向绑定的实现原理

至此,hello world双向绑定就基本实现了。文本内容会随输入框内容同步变化,在控制器中修改vm.text的值,会同步反映到文本内容中。

完整代码:https://github.com/bison1994/two-way-data-binding

总结

以上就是关于vue.js双向绑定实现原理的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
jquery last-child 列表最后一项的样式
Jan 22 Javascript
window.requestAnimationFrame是什么意思,怎么用
Jan 13 Javascript
Jquery的Tabs内容轮换效果实现代码,几行搞定
Feb 12 Javascript
JavaScript实现点击单元格改变背景色的方法
Feb 12 Javascript
JS实现重新加载当前页面
Nov 29 Javascript
Vue2.0组件间数据传递示例
Mar 07 Javascript
es6+angular1.X+webpack 实现按路由功能打包项目的示例
Aug 16 Javascript
你应该知道的几类npm依赖包管理详解
Oct 06 Javascript
Angular5中提取公共组件之radio list的实例代码
Jul 10 Javascript
Vue监听数据渲染DOM完以后执行某个函数详解
Sep 11 Javascript
了解重排与重绘
May 29 Javascript
vue实现一个6个输入框的验证码输入组件功能的实例代码
Jun 29 Javascript
微信小程序 底部导航栏目开发资料
Dec 05 #Javascript
基于js实现的限制文本框只可以输入数字
Dec 05 #Javascript
AJAX和jQuery动态加载数据的实现方法
Dec 05 #Javascript
Javascript中字符串replace方法的第二个参数探究
Dec 05 #Javascript
解析预加载显示图片艺术
Dec 05 #Javascript
JS限定手机版中图片大小随分辨率自动调整的方法
Dec 05 #Javascript
简单几步实现返回顶部效果
Dec 05 #Javascript
You might like
php实现邮件发送并带有附件
2014/01/24 PHP
thinkphp实现面包屑导航(当前位置)例子分享
2014/05/10 PHP
PHP定时更新程序设计思路分享
2014/06/10 PHP
php 使用curl模拟ip和来源进行访问的实现方法
2017/05/02 PHP
php实现表单提交上传文件功能
2018/05/28 PHP
ThinkPHP框架实现的邮箱激活功能示例
2018/06/15 PHP
基于Jquery插件开发之图片放大镜效果(仿淘宝)
2011/11/19 Javascript
JavaScript中Function详解
2015/02/27 Javascript
jQuery Validate初步体验(二)
2015/12/12 Javascript
BootStrap 轮播插件(carousel)支持左右手势滑动的方法(三种)
2016/07/07 Javascript
JS HTML5实现拖拽移动列表效果
2020/08/27 Javascript
jQuery实现的图片轮播效果完整示例
2016/09/12 Javascript
关于Vue实现组件信息的缓存问题
2017/08/23 Javascript
jquery插件开发之选项卡制作详解
2017/08/30 jQuery
解决微信小程序中转换时间格式IOS不兼容的问题
2019/02/15 Javascript
少女风vue组件库的制作全过程
2019/05/15 Javascript
jQuery创建折叠式菜单
2019/06/15 jQuery
VUE 自定义组件模板的方法详解
2019/08/30 Javascript
微信小程序和H5页面间相互跳转代码实例
2019/09/19 Javascript
高性能js数组去重(12种方法,史上最全)
2019/12/21 Javascript
通过实例解析JavaScript for in及for of区别
2020/06/15 Javascript
Python读取mp3中ID3信息的方法
2015/03/05 Python
python实现的简单猜数字游戏
2015/04/04 Python
Djang中静态文件配置方法
2015/07/30 Python
再谈Python中的字符串与字符编码(推荐)
2016/12/14 Python
python中字符串变二维数组的实例讲解
2018/04/03 Python
python3.7 的新特性详解
2019/07/25 Python
将keras的h5模型转换为tensorflow的pb模型操作
2020/05/25 Python
什么是网络协议
2016/04/07 面试题
银行实习生的自我评价
2013/12/09 职场文书
2014年商场工作总结
2014/11/22 职场文书
毕业设计指导教师评语
2014/12/30 职场文书
青年联谊会致辞
2015/07/31 职场文书
高三语文教学反思
2016/02/16 职场文书
《称赞》教学反思
2016/02/17 职场文书
Python 正则模块详情
2021/11/02 Python