什么是双向数据绑定?Vue是一个MVVM框架,数据绑定简单来说,就是当数据发生变化时,相应的视图会进行更新,当视图更新时,数据也会跟着变化。

实现数据绑定的方式大致有以下几种:

- 1、发布者-订阅者模式(backbone.js)
- 2、脏值检查(angular.js)
- 3、数据劫持(vue.js)

发布者-订阅者模式

一般通过sub, pub的方式实现数据和视图的绑定监听,更新数据方式通常做法是 vm.set('property', value),有兴趣可参考这里

我们更希望可以通过 vm.property = value 这种方式进行数据更新,同时自动更新视图。

脏值检查

angular是通过脏值检查方式来对比数据是否变化,来决定是否更新视图,最常见的方式是通过setInterval()来监测数据变化,当然,只会在某些指定事件触发时下才进行脏值检查。大致如下:

- DOM事件,譬如用户输入文本,点击按钮等。( ng-click )
- XHR响应事件 ( $http )
- 浏览器Location变更事件 ( $location )
- Timer事件( $timeout , $interval )
- 执行 $digest() 或 $apply()

数据劫持

Vue.js则是通过数据劫持以及结合发布者-订阅者来实现的,数据劫持是利用ES5的Object.defineProperty(obj, key, val)来劫持各个属性的的setter以及getter,在数据变动时发布消息给订阅者,从而触发相应的回调来更新视图。

一、实现最基础的数据绑定

<input type="text" id="in"/>
输入的值为:<span id="out"></span> <script>
var int = document.getElementById('in');
var out = document.getElementById('out');
var obj = {}; Object.defineProperty(obj, 'msg', {
enumerable: true,
configurable: true,
set (newVal) {
out.innerHTML = newVal;
}
}) int.addEventListener('input', function(e) {
obj.msg = e.target.value;
})
</script>

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性:里面有三个值,分别如下:

  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符
  • 一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。

有兴趣可以深入了解,这里就不多解释了=。=

二、双向数据绑定实现(此处用MVue替代)

上面的只是简单的使用了Object.defineProperty(),并不是我们最终想要的效果,最终想要的效果如下:

<div id="app">
<input type="text" v-model="text">
输入的值为:{{text}}
<div>
<input type="text" v-model="text">
</div>
</div>
<script>
var vm = new MVue({
el: '#app',
data: {
text: 'hello world'
}
})
</script>

实现思路:
1、输入框以及文本节点和data中的数据进行绑定
2、输入框内容变化时,data中的对应数据同步变化,即 view => model
3、data中数据变化时,对应的文本节点内容同步变化 即 model => view

上述流程如图所示:

1、实现一个数据监听器Obverser,对data中的数据进行监听,若有变化,通知相应的订阅者。
2、实现一个指令解析器Compile,对于每个元素上的指令进行解析,根据指令替换数据,更新视图。
3、实现一个Watcher,用来连接Obverser和Compile, 并为每个属性绑定相应的订阅者,当数据发生变化时,执行相应的回调函数,从而更新视图。
4、构造函数 (new MVue({}))

MVue构造函数

在初始化MVue实例时,对data中每个属性劫持监听,同时进行模板编译,指令解析,最后挂载到相应的DOM中。

function MVue (options) {
this.$el = options.el;
this.$data = options.data; // 初始化操作,后面会说
// ...
}

1、实现 view => model

DocumentFragment(文档片段)

vue进行编译时,将挂载目标的所有子节点劫持到DocumentFragment中,经过一份解析等处理后,再将DocumentFragment整体挂载到目标节点上。

function nodeToFragment (node, vm) {
var flag = document.createDocumentFragment();
var child;
while (child = node.firstChild) {
compile(child, vm);
if (child.firstChild) {
var dom = nodeToFragment(child, vm);
child.appendChild(dom);
}
flag.appendChild(child);
}
return flag;
}

模板编译(指令解析,事件绑定、初始化数据绑定)

编译过程图

代码如下:

function compile (node, vm) {
let reg = /\{\{(.*)\}\}/;
// 元素节点
if (node.nodeType === 1) {
var attrs = node.attributes;
for (let attr of attrs) {
if (attr.nodeName === 'v-model') {
// 获取v-model指令绑定的data属性
var name = attr.nodeValue;
// 绑定事件
node.addEventListener('input', function(e) {
vm.$data[name] = e.target.value;
})
// 初始化数据绑定
node.value = vm.$data[name];
// 移除v-model 属性
node.removeAttribute('v-model')
}
}
} // 文本节点
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1 && (RegExp.$1.trim());
// 绑定数据到文本节点中
node.nodeValue = node.nodeValue.replace(new RegExp('\\{\\{\\s*(' + name + ')\\s*\\}\\}'), vm.$data[name]);
}
}
}

现在,我们修改下MVue构造函数,增加模板编译,如下:

function MVue (options) {
this.$el = options.el;
this.$data = options.data; // 模板编译
let elem = document.querySelector(this.$el);
elem.appendChild(nodeToFragment(elem, this))
}

那么,我们的view => model 已经实现了,包括初始化绑定默认值,只要修改了input中的值,data中对应的值相应变化,并触发了setter, 更新属性值等(可以自行在set方法中打印看效果,或者在控制台手动输入vm.$data.text也会看到效果)。
2、实现 model => view
上面可以看出,虽然我们实现了初始化数据绑定,以及输入框变化时,data中text也会变化,但是文本节点仍然没有任何变化,那么如果做到文本节点也同步变化呢,这里用的是发布者-订阅者模式。

vue双向绑定的原理的更多相关文章

  1. vue双向绑定的原理及实现双向绑定MVVM源码分析

    vue双向绑定的原理及实现双向绑定MVVM源码分析 双向数据绑定的原理是:可以将对象的属性绑定到UI,具体的说,我们有一个对象,该对象有一个name属性,当我们给这个对象name属性赋新值的时候,新值 ...

  2. 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定,原生十行代码让你看懂原理

    废话不多说直接看效果图 代码很好理解,但是在看代码之前需要知道Vue双向绑定的原理其实就是基于Object.defineProperty 实现的双向绑定 官方传送门 这里我们用官方的话来说Object ...

  3. Vue双向绑定实现原理demo

    一.index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> ...

  4. vue双向绑定原理分析

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/jiangzhenf ...

  5. Vue双向绑定原理及其实现

    在之前面试的时候被面试官问到是否了解Vue双向绑定的原理,其实自己之前看过双向绑定的原理,但也就是粗略的了解,但是没有深入.面试官当时让我手写一个原理,但是就蒙了

  6. vue双向绑定原理源码解析

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/maxlove123 ...

  7. 关于网上大量对Vue双向绑定的错误理解

    对于Vue的双向绑定,现在基本是个前端都听说过,面试官也喜欢问这个问题.但对于Vue双向绑定的原理,掘金.博客园和segmentfault等技术社区充斥着大量的错误文章.这些文章的题目基本样子如下 “ ...

  8. Vue双向绑定原理,教你一步一步实现双向绑定

    当今前端天下以 Angular.React.vue 三足鼎立的局面,你不选择一个阵营基本上无法立足于前端,甚至是两个或者三个阵营都要选择,大势所趋. 所以我们要时刻保持好奇心,拥抱变化,只有在不断的变 ...

  9. 梳理vue双向绑定的实现原理

    Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后 ...

随机推荐

  1. Mysql优化-分区

    分区简介 分区是根据一定的规则,数据库把一个表分解成多个更小的.更容易管理的部分.就访问数据库应用而言,逻辑上就只有一个表或者一个索引,但实际上这个表可能有N个物理分区对象组成,每个分区都是一个独立的 ...

  2. linux 7 系统查询相关

    今天 领导让我整理一下查询关于系统信息的一些命令,查找并验证了之后,我发上来给大家分享一下. 1. #uname –a显示电脑及操作系统信息2. #cat /proc/version查看正在运行的内核 ...

  3. Struts2开发环境搭建

    eclipse配置struts2开发环境: 1.导入jar包:复制Struts\apps\struts2-blank\WEB-INF\lib下的所有jar包到当前项目的lib文件夹下 2.在web.x ...

  4. backface-visibility:hidden

    backface-visibility:hidden 不面向屏幕时隐藏

  5. CSIC_716_20191206【并发编程理论基础】

    进程:正在执行的一个过程,进程是对正在执行过程的一个抽象.区别于程序, 进程的三种状态:  进程是动态的. 就绪态ready:   进程具备运行状态,等待操作系统分配处理器 运行状态running:进 ...

  6. wordpress添加视频弹窗插件Video PopUp

    Video PopUp 给外部div 添加class类名:class="main-play" a链接添加  class="vp-a" 测试链接地址:https: ...

  7. delphi 文件存取方法与文件管理组件

    9.2  文件存取方法与文件管理组件 9.2.1  存取文件的类方法 Delphi在许多需要与文件打交道的类中定义了文件存取方法,使用这些方法可以非常方便地将类中的数据保存到文件中,或从文件中读取所需 ...

  8. ThinkPHP v5 新漏洞攻击案例首曝光,阿里云已可告警并拦截

    2018年12月10日,ThinkPHP v5系列发布安全更新,修复了一处可导致远程代码执行的严重漏洞.阿里云态势感知已捕获多起基于该漏洞的真实攻击,并对该漏洞原理以及漏洞利用方式进行分析.现在,对于 ...

  9. thinkphp 标签库驱动

    任何一个模板引擎的功能都不可能是为你量身定制的,具有一个良好的可扩展机制也是模板引擎的另外一个考量,Smarty采用的是插件方法来实现扩展,Think\Template由于采用了标签库技术,比Smar ...

  10. 扫描线+树状数组——cf1191F

    把所有点离散化,虚构一根扫描线从上往下扫,每行的点从左往右算贡献,开一个树状数组维护每个离散化后的x坐标是否已经有点 扫描到一个点时,先把这个点更新到树状数组里,每个点的贡献是它左边的所有点数*到它相 ...