Vue.js是MVVM结构,AngularJs是类似的;至于MVC,MVP,MVVM的对比,网上有很多,我就不赘述了。本文将为您深入介绍vue.js双向绑定的实现原理。有需要的朋友可以参考参考。让我们来看看边肖。
前言
众所周知,Vue.js有两个核心功能,一个是响应式数据绑定系统,一个是组件系统。本文只探讨hello world双向绑定是如何实现的,几乎所有Vue的开篇介绍中都有提及。先讲涉及的知识点,再参考源代码,用尽量少的代码实现hello world的开篇例子。
一、访问器属性
accessor属性是对象中的一个特殊属性,不能在对象中直接设置,而必须由defineProperty()方法单独定义。
var obj={ };
//为obj定义名为hello的访问器属性
Object.defineProperty(obj, hello ,{
get: function () {return sth},
set: function (val) {/* do sth */}
})
Obj.hello //可以像读取普通属性一样读取访问器属性。
访问器属性的“值”是特殊的。读取或设置accessor属性的值实际上是调用其内部特性:get和set函数。
Obj.hello //读取属性意味着调用get函数并返回get函数的返回值。
Obj.hello=abc //给属性赋值意味着调用set函数。赋值实际上是传递一个参数。
get和set方法中都指向obj,这意味着get和set函数可以操作对象内部的值。另外,accessor属性会‘覆盖’同名的common属性,因为accessor属性会先被访问,同名的common属性会被忽略(也就是所谓的‘劫持’)。
二、极简双向绑定的实现
这个例子的效果是:随着文本框中输入文本的变化,相同的文本内容会在span中同步显示;在js或console中显式修改obj.name的值,视图会相应更新。这样就实现了模型=视图和视图=模型的双向绑定,有求必应。
以上是Vue双向绑定的基本原理。
三、分解任务
上面的例子只是为了说明原理。
我们最终想要实现的是:
首先,将任务分成几个子任务:
1.输入框和文本节点与数据之间的数据绑定
2.当输入框的内容发生变化时,数据中的数据同步变化。也就是观点的改变=模式。
3.当数据中的数据发生变化时,文本节点的内容也会同步变化。也就是模型=视图的变化。
为了完成任务一,您需要编译DOM。这里有一个知识点:DocumentFragment。
四、DocumentFragment
DocumentFragment(文档片段)可以看作是一个节点容器,它可以包含多个子节点。当我们将它插入到DOM中时,只有它的子节点会被插入到目标节点中,所以把它看作是一组节点的容器。使用DocumentFragment处理节点,速度和性能远远优于直接操作DOM。Vue编译的时候,把挂载目标的所有子节点劫持(真的劫持)到DocumentFragment,经过一些处理,返回整个DocumentFragment,插入挂载目标。
五、数据初始化绑定
上面的代码实现了任务一,我们可以看到hello world已经呈现在输入框和文本节点中。
六、响应式的数据绑定
我们来看任务2的实现思路:当我们在输入框中输入数据时,首先触发输入事件(或者keyup和change事件)。在相应的事件处理程序中,我们获取输入框的值,并将其赋给vm实例的text属性。我们会使用defineProperty劫持数据中的文本作为vm的visitor属性,所以给vm.text赋值会触发set方法。
在set方法中,主要做两件事,第一是更新属性的值,第二是等到第三个任务。
第二个任务完成后,text属性的值将与输入框的内容同步变化:
七、订阅/发布模式(subscribepublish)
text属性已经改变,set方法已经触发,但是text节点的内容没有改变。如何让同样绑定到文本的文本节点同步变化?这里还有一个知识点:订阅发布模式。
订阅模式(也叫观察者模式)定义了一对多的关系,多个观察者同时监听一个topic对象,当topic对象的状态发生变化时,所有观察者都会得到通知。
发布者发送通知=主题对象接收通知并将其推送给订阅者=订阅者执行相应的操作。
如前所述,触发set方法的第二件事是以发布者的身份发布通知:“我是属性文本,我已更改”。文本节点充当订阅者,在收到消息后执行相应的更新操作。
八、双向绑定的实现
回过头来看,每当创建一个新的Vue,主要做两件事:第一件是监控数据:observe(data),第二件是编译HTML: nodefragment (id)。
在监控数据的过程中,会为数据中的每个属性生成一个主题对象dep。
在编译HTML的过程中,会为每个与数据绑定相关的节点生成一个订阅者观察器,观察器会将自己添加到对应属性的dep中。
我们实现了:修改输入框内容=修改事件回调函数中的属性值=触发属性的set方法。
接下来我们要实现的是:发出通知dep.notify()=触发订阅者的更新方法=更新视图。
这里的关键逻辑是:如何将watcher添加到关联属性的dep中。
在编译HTML的过程中,会为每个与数据相关联的节点生成一个观察器。Watcher函数中发生了什么?
首先,将自己分配给一个全局变量Dep.target;
其次,执行update方法,然后执行get方法。get方法读取vm的visitor属性,触发visitor属性的get方法。观察器被添加到get方法中相应visitor属性的dep中。
同样,获取属性的值,然后更新视图。
最后,将Dep.target设置为null。因为它是一个全局变量,并且是watcher和dep之间的唯一桥梁,所以Dep.target在任何时候都只能有一个值。
至此,hello world的双向绑定基本实现。内容将与输入框的内容同步变化。修改控制器中vm.text的值会同步反映到文本内容中。
完整代码:https://github.com/bison1994/two-way-data-binding
总结
以上就是关于vue.js双向绑定的实现原理希望这篇文章的内容能给你的学习或者工作带来一些帮助。有问题可以留言交流。谢谢你的支持。