Vue.js双向绑定实现原理详解


Posted in Javascript onDecember 22, 2016

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

参考文章:https://3water.com/article/100819.htm

一、访问器属性

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

var obj = { };

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

Object.defineProperty(obj, "hello", {

get: function () {return sth},

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

})

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

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

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

obj.hello = "abc" // 为属性赋值,就是调用set函数,赋值其实是传参

Vue.js双向绑定实现原理详解

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

二、极简双向绑定的实现

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事件(或者keyup、change事件),在相应的事件处理程序中,我们获取输入框的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双向绑定实现原理详解

至此,hello world双向绑定就基本实现了。文本内容会随输入框内容同步变化,在控制器中修改vm.text的值,会同步反映到文本内容中(但输入框还没有绑定,读者可以自己试试)。

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Extjs中常用表单介绍与应用
Jun 07 Javascript
仅IE9/10同时支持script元素的onload和onreadystatechange事件分析
Apr 27 Javascript
基于jQuery的图片剪切插件
Aug 03 Javascript
JS获取计算机mac地址以及IP的实现方法
Jan 08 Javascript
js动态移动滚动条至底部示例代码
Apr 24 Javascript
jquery实现的鼠标下拉滚动置顶效果
Jul 24 Javascript
D3.js进阶系列之CSV表格文件的读取详解
Jun 06 Javascript
jQuery zTree 异步加载添加子节点重复问题
Nov 29 jQuery
Vue通过ref父子组件拿值方法
Sep 12 Javascript
react 移动端实现列表左滑删除的示例代码
Jul 04 Javascript
vue 组件内获取actions的response方式
Nov 08 Javascript
jQuery 实现扁平式小清新导航
Jul 07 jQuery
BootStrap Table对前台页面表格的支持实例讲解
Dec 22 #Javascript
JS实现的简单轮播图运动效果示例
Dec 22 #Javascript
基于BootStrap与jQuery.validate实现表单提交校验功能
Dec 22 #Javascript
详解js的事件代理(委托)
Dec 22 #Javascript
HTML页面定时跳转方法解析(2种任选)
Dec 22 #Javascript
vue双向绑定的简单实现
Dec 22 #Javascript
jQuery使用Layer弹出层插件闪退问题
Dec 22 #Javascript
You might like
全国FM电台频率大全 - 6 辽宁省
2020/03/11 无线电
提升PHP速度全攻略
2006/10/09 PHP
PHP+APACHE实现用户论证的方法
2006/10/09 PHP
php实现数组筛选奇数和偶数示例
2014/04/11 PHP
PHP动态生成javascript文件的2个例子
2014/04/11 PHP
PHP批量删除、清除UTF-8文件BOM头的代码实例
2014/04/14 PHP
PHP 观察者模式深入理解与应用分析
2019/09/25 PHP
根据判断浏览器类型屏幕分辨率自动调用不同CSS的代码
2007/02/22 Javascript
基于JQuery制作的产品广告效果
2010/12/08 Javascript
Javascript 面向对象(三)接口代码
2012/05/23 Javascript
获取select元素被选中的文本内容的js代码
2014/01/29 Javascript
jQuery实现Twitter的自动文字补齐特效
2014/11/28 Javascript
JavaScript数据类型之基本类型和引用类型的值
2015/04/01 Javascript
jQuery实现TAB选项卡切换特效简单演示
2016/03/04 Javascript
Bootstrap登陆注册页面开发教程
2016/07/12 Javascript
JS中mouseup事件丢失的原因与解决办法
2017/06/14 Javascript
JavaScript内存泄漏的处理方式
2017/11/20 Javascript
JavaScript设计模式之工厂模式和抽象工厂模式定义与用法分析
2018/07/26 Javascript
推荐15个最好用的JavaScript代码压缩工具
2019/02/13 Javascript
浏览器事件循环与vue nextTicket的实现
2019/04/16 Javascript
vue实现密码显示与隐藏按钮的自定义组件功能
2019/04/23 Javascript
浅析vue-cli3配置webpack-bundle-analyzer插件【推荐】
2019/10/23 Javascript
Vue-cli项目部署到Nginx服务器的方法
2019/11/01 Javascript
详解vue-flickity的fullScreen功能实现
2020/04/07 Javascript
pymssql数据库操作MSSQL2005实例分析
2015/05/25 Python
Windows上使用virtualenv搭建Python+Flask开发环境
2016/06/07 Python
Python竟能画这么漂亮的花,帅呆了(代码分享)
2017/11/15 Python
python基于物品协同过滤算法实现代码
2018/05/31 Python
python矩阵转换为一维数组的实例
2018/06/05 Python
python对Excel按条件进行内容补充(推荐)
2019/11/24 Python
使用opencv将视频帧转成图片输出
2019/12/10 Python
解决Ubuntu18中的pycharm不能调用tensorflow-gpu的问题
2020/09/17 Python
解决CSS3的opacity属性带来的层叠顺序问题
2016/05/09 HTML / CSS
Gap加拿大官网:Gap Canada
2017/08/24 全球购物
计算机专业自荐信
2014/05/24 职场文书
没有孩子的离婚协议书怎么写
2014/09/17 职场文书