深入理解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 相关文章推荐
TFDN图片播放器 不错自动播放
Oct 03 Javascript
javascript入门·动态的时钟,显示完整的一些方法,新年倒计时
Oct 01 Javascript
JavaScript入门教程(3) js面向对象
Jan 31 Javascript
利用WebBrowser彻底解决Web打印问题(包括后台打印)
Jun 22 Javascript
WEB 浏览器兼容 推荐收藏
May 14 Javascript
使用text方法获取Html元素文本信息示例
Sep 01 Javascript
javascript顺序加载图片的方法
Jul 18 Javascript
jQuery支持添加事件的日历特效代码分享(3种样式)
Aug 24 Javascript
JavaScript兼容浏览器FF/IE技巧
Aug 14 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
Oct 01 Javascript
ES6 javascript的异步操作实例详解
Oct 30 Javascript
浅谈Vue.use的使用
Aug 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 简单日历实现代码
2009/10/28 PHP
php 无限级分类学习参考之对ecshop无限级分类的解析 带详细注释
2010/03/23 PHP
php中常用的预定义变量小结
2012/05/09 PHP
php随机输出名人名言的代码
2012/10/07 PHP
S2SH整合JQuery+Ajax实现登录验证功能实现代码
2013/01/30 Javascript
浅析jQuery对select操作小结(遍历option,操作option)
2013/07/04 Javascript
深入探寻seajs的模块化与加载方式
2015/04/14 Javascript
JS简单实现多级Select联动菜单效果代码
2015/09/06 Javascript
如何解决手机浏览器页面点击不跳转浏览器双击放大网页
2016/07/01 Javascript
基于jQuery实现歌词滚动版音乐播放器的代码
2016/09/17 Javascript
JavaScript利用闭包实现模块化
2017/01/13 Javascript
基于JavaScript实现移动端无限加载分页
2017/03/27 Javascript
详解vue组件通信的三种方式
2017/06/30 Javascript
客户端(vue框架)与服务器(koa框架)通信及服务器跨域配置详解
2017/08/26 Javascript
vue中添加mp3音频文件的方法
2018/03/02 Javascript
深入浅析Node环境和浏览器的区别
2018/08/14 Javascript
基于nodejs的雪碧图制作工具的示例代码
2018/11/05 NodeJs
echarts大屏字体自适应的方法步骤
2019/07/12 Javascript
Vue.js下拉菜单组件使用方法详解
2019/10/19 Javascript
Django rest framework基本介绍与代码示例
2018/01/26 Python
python链接oracle数据库以及数据库的增删改查实例
2018/01/30 Python
python实现图片识别汽车功能
2018/11/30 Python
Python初学者常见错误详解
2019/07/02 Python
pandas read_excel()和to_excel()函数解析
2019/09/19 Python
详解基于python-django框架的支付宝支付案例
2019/09/23 Python
python根据文本生成词云图代码实例
2019/11/15 Python
学Python 3的理由和必要性
2019/11/19 Python
python名片管理系统开发
2020/06/18 Python
华为旗下电子商务平台:华为商城
2016/08/06 全球购物
伦敦一卡通:The London Pass
2018/11/30 全球购物
Kate Spade澳大利亚官方网站:美国设计师手袋品牌
2019/09/10 全球购物
中专药剂专业应届毕的自我评价
2013/12/27 职场文书
家长对老师的评语
2014/04/18 职场文书
MySQL安装后默认自带数据库的作用详解
2021/04/27 MySQL
Java实现带图形界面的聊天程序
2022/06/10 Java/Android
向Spring IOC 容器动态注册bean实现方式
2022/07/15 Java/Android