Vue.js双向绑定的实现原理
Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统。本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的。先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例。
参考文章:https://segmentfault.com/a/1190000006599500
一、访问器属性
访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过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函数,赋值其实是传参 
get和set方法内部的this都指向obj,这意味着get和set函数可以操作对象内部的值。另外,访问器属性的会"覆盖"同名的普通属性,因为访问器属性会被优先访问,与其同名的普通属性则会被忽略(也就是所谓的被"劫持"了)。
二、极简双向绑定的实现

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

以上就是Vue实现双向绑定的基本原理。
三、分解任务
上述示例仅仅是为了说明原理。我们最终要实现的是:


首先将该任务分成几个子任务:
1、输入框以及文本节点与data中的数据绑定
2、输入框内容变化时,data中的数据同步变化。即view => model的变化。
3、data中的数据变化时,文本节点的内容同步变化。即model => view的变化。
要实现任务一,需要对DOM进行编译,这里有一个知识点:DocumentFragment。
四、DocumentFragment
DocumentFragment(文档片段)可以看作节点容器,它可以包含多个子节点,当我们将它插入到DOM中时,只有它的子节点会插入目标节点,所以把它看作一组节点的容器。使用DocumentFragment处理节点,速度和性能远远优于直接操作DOM。Vue进行编译时,就是将挂载目标的所有子节点劫持(真的是劫持)到DocumentFragment中,经过一番处理后,再将DocumentFragment整体返回插入挂载目标。


五、数据初始化绑定



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

六、响应式的数据绑定
再来看任务二的实现思路:当我们在输入框输入数据的时候,首先触发input事件(或者keyup、change事件),在相应的事件处理程序中,我们获取输入框的value并赋值给vm实例的text属性。我们会利用defineProperty将data中的text劫持为vm的访问器属性,因此给vm.text赋值,就会触发set方法。在set方法中主要做两件事,第一是更新属性的值,第二留到任务三再说。


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

七、订阅/发布模式(subscribe&publish)
text属性变化了,set方法触发了,但是文本节点的内容没有变化。如何让同样绑定到text的文本节点也同步变化呢?这里又有一个知识点:订阅发布模式。
订阅发布模式(又称观察者模式)定义了一种一对多的关系,让多个观察者同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察者对象。
发布者发出通知 => 主题对象收到通知并推送给订阅者 => 订阅者执行相应操作

之前提到的,当set方法触发后做的第二件事就是作为发布者发出通知:“我是属性text,我变了”。文本节点则是作为订阅者,在收到消息后执行相应的更新操作。
八、双向绑定的实现
回顾一下,每当new一个Vue,主要做了两件事:第一个是监听数据:observe(data),第二个是编译HTML:nodeToFragement(id)。
在监听数据的过程中,会为data中的每一个属性生成一个主题对象dep。
在编译HTML的过程中,会为每个与数据绑定相关的节点生成一个订阅者watcher,watcher会将自己添加到相应属性的dep中。
我们已经实现:修改输入框内容 => 在事件回调函数中修改属性值 => 触发属性的set方法。
接下来我们要实现的是:发出通知dep.notify() => 触发订阅者的update方法 => 更新视图。
这里的关键逻辑是:如何将watcher添加到关联属性的dep中。

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

首先,将自己赋给了一个全局变量Dep.target;
其次,执行了update方法,进而执行了get方法,get的方法读取了vm的访问器属性,从而触发了访问器属性的get方法,get方法中将该watcher添加到了对应访问器属性的dep中;
再次,获取属性的值,然后更新视图。
最后,将Dep.target设为空。因为它是全局变量,也是watcher与dep关联的唯一桥梁,任何时刻都必须保证Dep.target只有一个值。


至此,hello world双向绑定就基本实现了。文本内容会随输入框内容同步变化,在控制器中修改vm.text的值,会同步反映到文本内容中(但输入框还没有绑定,读者可以自己试试)。
完整代码:https://github.com/bison1994/two-way-data-binding
Vue.js双向绑定的实现原理的更多相关文章
- Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)
Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...
- Vue.js双向绑定原理
Vue.js最核心的功能有两个,一个是响应式的数据绑定系统,另一个是组件系统.本文仅仅探究双向绑定是怎样实现的.先讲涉及的知识点,再用简化的代码实现一个简单的hello world示例. 一.访问器属 ...
- vue实现双向绑定的简单原理: defineProperty
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue.js双向绑定之--select获取text
在大多数情况下select下拉菜单都是value和text设置不同的值的,value一般来说是与后台交互的值,而text是前端用来显示的文本: 但是,vue.js对到表单的双向绑定时如果option设 ...
- vue的双向绑定原理及实现
前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...
- 西安电话面试:谈谈Vue数据双向绑定原理,看看你的回答能打几分
最近我参加了一次来自西安的电话面试(第二轮,技术面),是大厂还是小作坊我在这里按下不表,先来说说这次电面给我留下印象较深的几道面试题,这次先来谈谈Vue的数据双向绑定原理. 情景再现: 当我手机铃声响 ...
- Vue数据双向绑定原理及简单实现
嘿,Goodgirl and GoodBoy,点进来了就看完点个赞再go. Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. ...
- 梳理vue双向绑定的实现原理
Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后 ...
- vue数据双向绑定原理
vue的数据双向绑定的小例子: .html <!DOCTYPE html> <html> <head> <meta charset=utf-> < ...
随机推荐
- 【Network】UDP 大包怎么发? MTU怎么设置?
这里主要用UDP来发送视频,当发送的数据大于1500时分包发送,保证每包小于1500.发送好办,分割后循环发就可以了,关键是接收时的处理.先做一下处理的方法 :发送时每包上面加上标识,比如RTP的做法 ...
- Longest Absolute File Path
Suppose we abstract our file system by a string in the following manner: The string "dir\n\tsub ...
- java19
1:异常(理解) (1)程序出现的不正常的情况. (2)异常的体系 Throwable |--Error 严重问题,我们不处理. |--Exception |--RuntimeException 运行 ...
- AngularJS 初用总结
一直用惯jquery,初用Angularjs的初段时间,需要先了解一下她的类MVC思想. 初学时几个比较基本的概念: 1.客户端模板 2. Model View Controller (MVC) 3. ...
- Resource Acquisition Is Initialization(RAII Idiom)
原文链接:http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Resource_Acquisition_Is_Initialization Intent ...
- iOS 8.0后使用UIAlertController
iOS 8的新特性之一就是让接口更有适应性.更灵活,因此许多视图控制器的实现方式发生了巨大的变化.全新的UIPresentationController在实现视图控制器间的过渡动画效果和自适应设备尺寸 ...
- epoll示例
书到用时方恨少,一切尽在不言中 #include <iostream> #include <sys/socket.h> #include <sys/epoll.h> ...
- OpenStack 云计算基础知识
OpenStack Docs: Currenthttp://docs.openstack.org/ OpenStack云计算快速入门教程 - OpenStack及其构成简介_服务器应用_Linux公社 ...
- 《图形学》实验七:中点Bresenham算法画椭圆
开发环境: VC++6.0,OpenGL 实验内容: 使用中点Bresenham算法画椭圆. 实验结果: 代码: #include <gl/glut.h> #define WIDTH 50 ...
- node+fis3搭建
node安装: 到https://nodejs.org/en/download/releases下载编译好的包, 如:https://nodejs.org/download/release/v4.4. ...