您现在的位置是:网站首页> 编程资料编程资料

 面试问题Vue双向数据绑定原理_vue.js_

2023-05-24 314人已围观

简介  面试问题Vue双向数据绑定原理_vue.js_

前言

vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。

具体步骤

  • 1、需要observer的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter 这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
  • 2、compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  • 3、Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
    (1)在自身实例化时往属性订阅器(dep)里面添加自己
    (2)自身必须有一个update()方法
    (3)待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
  • 4、MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

什么是数据双向绑定

vue是一个mvvm框架,即数据双向绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。这也算是vue的精髓之处了。值得注意的是, 我们所说的数据双向绑定,一定是对于UI控件来说的,非UI控件不会涉及到数据双向绑定。 单向数据绑定是使用状态管理工具(如redux)的前提。如果我们使用vuex,那么数据流也是单项的,这时就会和双向数据绑定有冲突,我们可以这么解决

为什么要实现数据的双向绑定

在vue中,如果使用vuex,实际上数据还是单向的,之所以说是数据双向绑定,这是用的UI控件来说,对于我们处理表单,vue的双向数据绑定用起来就特别舒服了。

即两者并不互斥, 在全局性数据流使用单项,方便跟踪; 局部性数据流使用双向,简单易操作。

什么是Object.defineProperty?

 语法:

Object.defineProperty(obj, prop, descriptor)

参数说明:

  • obj:必需。目标对象
  • prop:必需。需定义或修改的属性的名字
  • descriptor:必需。目标属性所拥有的特性

返回值:

传入函数的对象。即第一个参数obj;

针对属性,我们可以给这个属性设置一些特性,比如是否只读不可以写;是否可以被for…in或Object.keys()遍历。

给对象的属性添加特性描述,目前提供两种形式:数据描述和存取器描述。

当修改或定义对象的某个属性的时候,给这个属性添加一些特性:

一、访问器属性

Object.defineProperty()函数可以定义对象的属性相关描述符, 其中的set和get函数对于完成数据双向绑定起到了至关重要的作用,下面,我们看看这个函数的基本使用方式。

var obj = { foo: 'foo' } Object.defineProperty(obj, 'foo', { get: function () { console.log('将要读取obj.foo属性'); }, set: function (newVal) { console.log('当前值为', newVal); } }); obj.foo; // 将要读取obj.foo属性 obj.foo = 'name'; // 当前值为 name

可以看到,get即为我们访问属性时调用,set为我们设置属性值时调用。

二、简单的数据双向绑定实现方法

forvue 输入:

最终效果图:

可以看到,实现一个简单的数据双向绑定还是不难的: 使用Object.defineProperty()来定义属性的set函数,属性被赋值的时候,修改Input的value值以及span中的innerHTML;然后监听input的keyup事件,修改对象的属性值,即可实现这样的一个简单的数据双向绑定。

双向绑定指令为v-model:

Vue入门之htmlraw

您输入的信息是:{{ msg }}

最终的结果就是:你改变 input 文本框的内容的时候,p 标签中的内容会跟着进行改变。

三、 实现任务的思路

上面我们只是实现了一个最简单的数据双向绑定,而我们真正希望实现的时下面这种方式:

{{ text }}

即和vue一样的方式来实现数据的双向绑定。那么,我们可以把整个实现过程分为下面几步:

  • 输入框以及文本节点与 data 中的数据绑定
  • 输入框内容变化时,data 中的数据同步变化。即 view => model 的变化
  • data 中的数据变化时,文本节点的内容同步变化。即 model => view 的变化

四、DocumentFragment

如果希望实现任务一,我们还需要使用到 DocumentFragment 文档片段,可以把它看做一个容器,如下所示:

这样,我们就可以得到下面的DOM树:

使用文档片段的好处在于:在文档片段上进行操作DOM,而不会影响到真实的DOM,操作完成之后,我们就可以添加到真实DOM上,这样的效率比直接在正式DOM上修改要高很多 。

vue进行编译时,就是将挂载目标的所有子节点劫持到DocumentFragment中,经过一番处理之后,再将DocumentFragment整体返回插入挂载目标

如下所示 :

forvue

即首先获取到div,然后通过documentFragment劫持,接着再把这个文档片段添加到div上去。

五、初始化数据绑定

forvue
{{ text }}

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

六、响应式的数据绑定

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

forvue
{{ text }}