源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

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

  • 时间:2021-03-28 19:13 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:深入理解vue.js双向绑定的实现原理
[b]前言[/b] 大家都知道Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统。本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的。先讲涉及的知识点,再参考源码,用尽可能少的代码实现那个hello world开篇示例。 [b]一、访问器属性[/b] 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过[code]defineProperty()[/code]方法单独定义。
var obj = { };

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

Object.defineProperty(obj, "hello", {

get: function () {return sth},

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

})
[code]obj.hello[/code] // 可以像普通属性一样读取访问器属性 访问器属性的"值"比较特殊,读取或设置访问器属性的值,实际上是调用其内部特性:[code]get[/code]和[code]set[/code]函数。 [code]obj.hello[/code] // 读取属性,就是调用[code]get[/code]函数并返回[code]get[/code]函数的返回值 [code]obj.hello = "abc"[/code] // 为属性赋值,就是调用[code]set[/code]函数,赋值其实是传参[img]http://files.jb51.net/file_images/article/201612/201612051116511.png[/img] [code]get[/code]和[code]set[/code]方法内部的this都指向obj,这意味着[code]get[/code]和[code]set[/code]函数可以操作对象内部的值。另外,访问器属性的会"覆盖"同名的普通属性,因为访问器属性会被优先访问,与其同名的普通属性则会被忽略(也就是所谓的被"劫持"了)。 [b]二、极简双向绑定的实现[/b] [img]http://files.jb51.net/file_images/article/201612/201612051116522.png[/img] 此例实现的效果是:随文本框输入文字的变化,span中会同步显示相同的文字内容;在js或控制台显式的修改[code]obj.name[/code]的值,视图会相应更新。这样就实现了[code]model =>view[/code]以及[code]view => model[/code]的双向绑定,并且是响应式的。 [img]http://files.jb51.net/file_images/article/201612/201612051116523.png[/img] 以上就是Vue实现双向绑定的基本原理。 [b]三、分解任务[/b] 上述示例仅仅是为了说明原理。 我们最终要实现的是: [img]http://files.jb51.net/file_images/article/201612/201612051116524.png[/img] [img]http://files.jb51.net/file_images/article/201612/201612051116525.png[/img] 首先将该任务分成几个子任务:    1、输入框以及文本节点与data中的数据绑定    2、输入框内容变化时,data中的数据同步变化。即[code]view => model[/code]的变化。    3、data中的数据变化时,文本节点的内容同步变化。即[code]model => view[/code]的变化。 要实现任务一,需要对DOM进行编译,这里有一个知识点:DocumentFragment。 [b]四、DocumentFragment[/b] DocumentFragment(文档片段)可以看作节点容器,它可以包含多个子节点,当我们将它插入到DOM中时,只有它的子节点会插入目标节点,所以把它看作一组节点的容器。使用DocumentFragment处理节点,速度和性能远远优于直接操作DOM。Vue进行编译时,就是将挂载目标的所有子节点劫持(真的是劫持)到DocumentFragment中,经过一番处理后,再将DocumentFragment整体返回插入挂载目标。 [img]http://files.jb51.net/file_images/article/201612/201612051116526.png[/img] [img]http://files.jb51.net/file_images/article/201612/201612051116527.png[/img] [b]五、数据初始化绑定[/b] [img]http://files.jb51.net/file_images/article/201612/201612051116528.png[/img] [img]http://files.jb51.net/file_images/article/201612/201612051116529.png[/img] [img]http://files.jb51.net/file_images/article/201612/2016120511165210.png[/img] 以上代码实现了任务一,我们可以看到,hello world已经呈现在输入框和文本节点中。 [img]http://files.jb51.net/file_images/article/201612/2016120511165211.png[/img] [b]六、响应式的数据绑定[/b] 再来看任务二的实现思路:当我们在输入框输入数据的时候,首先触发[code]input[/code]事件(或者[code]keyup[/code]、[code]change[/code]事件),在相应的事件处理程序中,我们获取输入框的[code]value[/code]并赋值给vm实例的[code]text[/code]属性。我们会利用[code]defineProperty[/code]将data中的[code]text[/code]劫持为vm的访问器属性,因此给[code]vm.text[/code]赋值,就会触发[code]set[/code]方法。 在[code]set[/code]方法中主要做两件事,第一是更新属性的值,第二留到任务三再说。 [img]http://files.jb51.net/file_images/article/201612/2016120511165212.png[/img] [img]http://files.jb51.net/file_images/article/201612/2016120511165213.png[/img] 任务二也就完成了,text属性值会与输入框的内容同步变化: [img]http://files.jb51.net/file_images/article/201612/2016120511165214.png[/img] [b]七、订阅/发布模式(subscribe&publish)[/b] text属性变化了,[code]set[/code]方法触发了,但是文本节点的内容没有变化。如何让同样绑定到text的文本节点也同步变化呢?这里又有一个知识点:订阅发布模式。 订阅发布模式(又称观察者模式)定义了一种一对多的关系,让多个观察者同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察者对象。 发布者发出通知 => 主题对象收到通知并推送给订阅者 => 订阅者执行相应操作 [img]http://files.jb51.net/file_images/article/201612/2016120511165215.png[/img] 之前提到的,当[code]set[/code]方法触发后做的第二件事就是作为发布者发出通知:“我是属性text,我变了”。文本节点则是作为订阅者,在收到消息后执行相应的更新操作。 [b]八、双向绑定的实现[/b] 回顾一下,每当[code]new[/code]一个Vue,主要做了两件事:第一个是监听数据:[code]observe(data) [/code],第二个是编译HTML:[code]nodeToFragement(id)[/code] 。 在监听数据的过程中,会为data中的每一个属性生成一个主题对象dep。 在编译HTML的过程中,会为每个与数据绑定相关的节点生成一个订阅者watcher,watcher会将自己添加到相应属性的dep中。 我们已经实现:修改输入框内容 => 在事件回调函数中修改属性值 => 触发属性的[code]set[/code]方法。 接下来我们要实现的是:发出通知[code]dep.notify() [/code]=> 触发订阅者的[code]update[/code]方法 => 更新视图。 这里的关键逻辑是:如何将watcher添加到关联属性的dep中。 [img]http://files.jb51.net/file_images/article/201612/2016120511165216.png[/img] 在编译HTML过程中,为每个与data关联的节点生成一个Watcher。Watcher函数中发生了什么呢? [img]http://files.jb51.net/file_images/article/201612/2016120511165217.png[/img] 首先,将自己赋给了一个全局变量[code]Dep.target[/code]; 其次,执行了[code]update[/code]方法,进而执行了[code]get[/code]方法,[code]get[/code]的方法读取了vm的访问器属性,从而触发了访问器属性的[code]get[/code]方法,[code]get[/code]方法中将该watcher添加到了对应访问器属性的dep中; 再次,获取属性的值,然后更新视图。 最后,将[code]Dep.target[/code]设为空。因为它是全局变量,也是watcher与dep关联的唯一桥梁,任何时刻都必须保证[code]Dep.target[/code]只有一个值。 [img]http://files.jb51.net/file_images/article/201612/2016120511165218.png[/img] [img]http://files.jb51.net/file_images/article/201612/2016120511165219.png[/img] [img]http://files.jb51.net/file_images/article/201612/2016120511165220.png[/img] 至此,hello world双向绑定就基本实现了。文本内容会随输入框内容同步变化,在控制器中修改[code]vm.text[/code]的值,会同步反映到文本内容中。 完整代码:https://github.com/bison1994/two-way-data-binding [b]总结[/b] 以上就是关于vue.js双向绑定实现原理的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对编程素材网的支持。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部