tip:有问题或者需要大厂内推的+我脉脉哦:丛培森 ٩( ‘ω’ )و

【本文源址:http://blog.csdn.net/q1056843325/article/details/72999948 转载请添加该地址】

什么是双向数据绑定呢?
简单的说
就是UI视图与数据绑定在了一块
也就是数据和视图是同步改变的
双向数据绑定最常见的应用场景就是表单
(应用场景还是很有限的)


现在我们要实现这样一个简单的数据绑定
输入栏中输入字符
和它绑定的节点内容同步改变
此外还有一个按钮用于生成随机数改变input和div内的数据

首先我们先把需要把html的简单结构实现

<input id="input" data-bind="demo">
<div id="output" data-bind="demo"></div>
<button id="random">随机数</button>

还需要在js中获取这些DOM节点

let $ = document.querySelector.bind(document);
let $i = $('#input');
let $o = $('#output');
let $random = $('#random');

#简易实现
如果仅仅是为了实现这样的效果实际上非常简单
我们很容易就可以想到input事件,然后动态改变
那么我们首先就来简单的实现一下

let def = 'default';
$i.value = def;
$o.textContent = def;$i.oninput = function(){$o.textContent = $i.value;
}$random.onclick = function(){let rand = Math.floor(Math.random()*10e5)$i.value = rand;$o.textContent = rand;
}

虽然实现了效果
但是实际上只有视图改变,影响数据改变的过程
而且也没有把节点联系在一起

#数据模型(Ember.js原理)
Ember.js使用了这种数据模型的方法
虽然很麻烦,但是很容易让我们理解
实际上就是把数据还有要节点封装在了一起
这样后续的更新一定会经过这个模型
模型就了解了变化
从而做出处理

首先我们来实现一个数据模型的类
当我们需要绑定一组节点时
就可以实例化这个数据模型
(为了方便下面我都使用ES6语法)

class DataModel {constructor(str = ''){this.data = str;this.nodes = [];}bindTo(node){this.nodes.push(node);this.update();}update(){const INPUT_NODE = ['INPUT','TEXTAREA'];let {nodes} = this;for(let i = 0, node; node = nodes[i++];){if(INPUT_NODE.includes(node.nodeName)){if(node.value !== this.data){ //避免光标跳到尾部node.value = this.data;}}else{node.textContent = this.data;}}}set(str){if(str !== this.value){this.data = str;this.update();}}get(){return this.data;}
}

this.data就是我们模型的数据
this.nodes是我们绑定的节点列表
bindTo方法接受我们的Dom节点并传入节点列表
既然有新节点进加入组织了(节点绑定),那么也肯定要让它接受新的数据(数据与UI改变)
update方法用于更新视图,实际上是遍历所有绑定节点,判断类型然后做出改变

声明了数据模型类后我们就可以为input和div绑定到一个模型中了

let gModel = {demo: new DataModel('default')
};
//数据->视图
gModel[$i.getAttribute('data-bind')].bindTo($i);
gModel[$o.getAttribute('data-bind')].bindTo($o);

gModel是我们声明的一个全局数据模型对象
因为页面中不一定只有这一组数据绑定
$i$odata-bind属性值就相当于它们的“组织名”
这里我就起名demo了
使用模型的API来绑定这两个节点

//视图->数据
$i.addEventListener('input', function(){gModel[this.getAttribute('data-bind')].set(this.value);
});$random.onclick = function(){gModel.demo.set(Math.floor(Math.random()*10e5));
}

最后绑定input事件还有按钮的click事件
当输入值后,就改变这个模型的data
set方法改变data的同时还会触发所有与之绑定在一起的节点做出更新

#脏检查(Angular.js原理)
Augular.js采用脏检查的方式来实现双向数据绑定
其原理是不会去监听数据的变化
而是我觉得你可能要发生数据变化的时候(用户交互,DOM操作等)
就去检查你的所有数据,看看到底有没有变化
不过这个数据检查是组件级别的
虽然如此,很多时候还是会产生很多没用的检查

我们需要模拟以下组件的核心函数

class Scope {constructor(){this.nodes = [];this.watchers = [];}watch(watchExp, listener = function(){}){this.watchers.push({watchExp,listener});}digest(){let dirty;let {watchers} = this;do {dirty = false;for(var i = 0, watcher; watcher = watchers[i++];){let newValue = watcher.watchExp();let oldValue = watcher.last;if(newValue !== oldValue){dirty = true;watcher.listener(newValue, oldValue);watcher.last = newValue;}}}while(dirty);}update(newValue){const INPUT_NODE = ['INPUT','TEXTAREA'];let {nodes} = this;for(let i = 0, node; node = nodes[i++];){if(INPUT_NODE.includes(node.nodeName)){if(node.value !== newValue){node.value = newValue;}}else{node.textContent = newValue;}} }bindTo(node){let {nodes} = this;let key = node.getAttribute('data-bind');if(!key){return;}nodes.push(node);this.update(this[key]);this.watch(() => {return this[key];}, (newValue, oldValue) => {this.update(newValue);});}
}

区别于上一种方法
这里的this.nodes代表某组件的全部节点
this.watchers数组存储着watcher
每一个watcher封装着用于脏检查的函数
而watch方法就负责向watchers中添加watcher
它接受两个参数,一个取值函数watchExp和一个回调函数listener

digest方法会遍历整个watcher
last存储着上一个值,再通过取值函数获取值
通过比较可以知道值有没有变脏
如果脏了,就触发回调函数(渲染数据)并且更新last值
还要重新检查一遍watchers确保last和数据一致
(这里没有处理互相绑定死循环的问题,可以设置检查上限)

声明完组件类,我们就可以实例化一个组件
绑定节点,监听事件,还要手动进行脏检查

let scope = new Scope();
scope.demo = 'default';//数据->视图
scope.bindTo($i);
scope.bindTo($o);//视图->数据
$i.addEventListener('input', function(){scope[this.getAttribute('data-bind')] = this.value;scope.digest();
});$random.onclick = function(){scope.demo = Math.floor(Math.random()*10e5);scope.digest();
}

#访问器监听(Vue.js原理)
vue.js实现数据变化影响视图变化的方式便是利用了ES5的setter
数据改变,触发setter渲染视图
视图影响数据没什么好说的,肯定需要监听input事件

这里我就写的简单点了

let data = {};
let def = 'default';
$i.value = def;
$o.textContent = def;//数据->视图
Object.defineProperty(data, 'demo', {set: function(newValue){$i.value = newValue;$o.textContent = newValue;}
});//视图->数据
$i.addEventListener('input', function() {data[this.getAttribute('data-bind')] = this.value;
});$random.onclick = function() {data.demo = Math.floor(Math.random()*10e5);
};

实际上vue实现的要比这复杂多得多
因为setter在很多情况下并不是万金油
也就是说并不是对象属性的任何变动它都能够监听的到
比如说以下场景:

  • 向对象添加新属性
  • 删除现有属性
  • 数组改变

关于这些问题这里就不讨论了
如果有时间的同学可以去研究以下源码

此外还要说明一下
原本ES7草案中的Object.observe()由于严重的性能问题已经被移除了

主页传送门

JavaScript实现简单的双向数据绑定(Ember、Angular、Vue)相关推荐

  1. [转]js实现简单的双向数据绑定

    转自:http://www.see-source.com/blog/300000038/444 双向数据绑定指的就是,绑定对象属性的改变到用户界面的变化的能力,反之亦然.换种说法,如果我们有一个use ...

  2. 手写简单的双向数据绑定

    github.com/logictuLuoq- 简单介绍一下MVVM MVVM是Model-View-ViewModel的简写 MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI ...

  3. 双向数据绑定原理(Vue)

    双向绑定的底层原理就是应用了发布订阅者模式结合数据劫持,通过Object.defineProperty()方法来劫持各个属性(需要数据绑定的属性,vue 双花括号里面的值就是需要双向数据绑定的属性,如 ...

  4. 原生js简单实现双向数据绑定原理

    根据对象的访问器属性去监听对象属性的变化,访问器属性不能直接在对象中设置,而必须通过 defineProperty() 方法单独定义. 访问器属性的"值"比较特殊,读取或设置访问器 ...

  5. vue双向数据绑定没有生效----vue强制更新dom结构

    vue中使用v-for渲染的页面无法通过v-if或v-show动态隐藏 当你的数剧层次太多,数据是变化了的,但是dom没有更新,没有自动双向数据驱动 这时候可以手动强制刷新 this.$forceUp ...

  6. 【AngularJs】Angular双向数据绑定

    1.什么是双向数据绑定 双向数据绑定: Angular实现了双向绑定机制.所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面.即数据模型(Module)和视图(View)之 ...

  7. 分析Vue双向数据绑定原理以及简单实现MVVM

    前言 随着Vue的火热发展,越来越多的程序员并不满足于对框架的使用,更多地追求其内在的原理,就像不能沉沦于美丽的外表,更应该追求灵魂的高度. 正文 好了,废话不多说,接下来我们将通过俩方面开展我们对外 ...

  8. [Vue源码分析]谷歌翻译后,Vue双向数据绑定失效了?

    前言: 最近运营反馈了一个问题:谷歌浏览器打开第三方储值平台,使用谷歌浏览器自带的翻译功能后,选择商品没有计算总额. 首先可以肯定的是这不是bug,这个平台已经兼容了13种语言,只是运营没有通过语言栏 ...

  9. c语言一次绑定多个控件,一入前端深似海,从此红尘是路人系列第九弹之如何实现一个双向数据绑定...

    前言 简单介绍一下双向数据绑定实现的是一个什么样的东西.首先有两个东西,他们分别是: V-视图层 M-数据层 1.视图层传向数据层:V发生改变,M也发生改变 2.数据层传向视图层:M发生改变,V也发生 ...

最新文章

  1. 运用UE和Blocks,仅用两周打造一个VR游戏(52VR完整版译文)
  2. c 和java互相验签_C椭圆曲线签名 用java验签
  3. rootkit后门检测工具rkhunter
  4. Arduino驱动IIC/I2C LCD1602模块显示
  5. 不用ps如何批量修改图片尺寸?教你一个方法
  6. linux查看ip命令
  7. 作为程序员上班的第一天
  8. ffmpeg一些基本用法
  9. linux系统文件颜色所代表的意思
  10. vue拖拽辅助线+改变元素大小组件
  11. Mac 上删除键(delete)的妙用
  12. 高并发系统设计四(系统怎样做到高可用)
  13. Android 语音播报 , 百度在线语音合成封装;
  14. Mac苹果电脑上有哪些好用的txt小说阅读器?
  15. unity2D学习(10)创建敌人、为敌人编写简单的AI
  16. 关于uniapp编译小程序 引入全局scss问题解决方案
  17. oracle中把一个表中的数据更新到新表中
  18. 【转】Windows系统中简单卷、带区卷、跨区卷、镜像卷的测试对比
  19. IDEA创建ZK框架入门Hello word
  20. 加快构建工业互联网产业生态

热门文章

  1. 如何计算机器人的工作范围,工业机器人能做什么工作_工业机器人的工作范围...
  2. 如何申请 @msn.com 邮箱
  3. 嵌入式Linux_USB_U盘自动检测识别
  4. 程序员兼职私活的网站
  5. CIO40知识星球:参观富士康灯塔工厂
  6. 关于医保你可能不了解的
  7. Oracle 数据精度错误-ORA-01438
  8. 2021-07-10程序控制流程
  9. nginx的rewrite中last和break 及 permanent和redirect的介绍
  10. 升级windows11的方法