JavaScript实现简单的双向数据绑定(Ember、Angular、Vue)
2017-06-13 16:14
1926 查看
【本文源址:http://blog.csdn.net/q1056843325/article/details/72999948 转载请添加该地址】
什么是双向数据绑定呢?
简单的说
就是UI视图与数据绑定在了一块
也就是数据和视图是同步改变的
双向数据绑定最常见的应用场景就是表单
(应用场景还是很有限的)
现在我们要实现这样一个简单的数据绑定
输入栏中输入字符
和它绑定的节点内容同步改变
此外还有一个按钮用于生成随机数改变input和div内的数据
首先我们先把需要把html的简单结构实现
还需要在js中获取这些DOM节点
我们很容易就可以想到input事件,然后动态改变
那么我们首先就来简单的实现一下
虽然实现了效果
但是实际上只有视图改变,影响数据改变的过程
而且也没有把节点联系在一起
虽然很麻烦,但是很容易让我们理解
实际上就是把数据还有要节点封装在了一起
这样后续的更新一定会经过这个模型
模型就了解了变化
从而做出处理
首先我们来实现一个数据模型的类
当我们需要绑定一组节点时
就可以实例化这个数据模型
(为了方便下面我都使用ES6语法)
而
既然有新节点进加入组织了(节点绑定),那么也肯定要让它接受新的数据(数据与UI改变)
声明了数据模型类后我们就可以为input和div绑定到一个模型中了
因为页面中不一定只有这一组数据绑定
这里我就起名demo了
使用模型的API来绑定这两个节点
最后绑定input事件还有按钮的click事件
当输入值后,就改变这个模型的data
set方法改变data的同时还会触发所有与之绑定在一起的节点做出更新
其原理是不会去监听数据的变化
而是我觉得你可能要发生数据变化的时候(用户交互,DOM操作等)
就去检查你的所有数据,看看到底有没有变化
不过这个数据检查是组件级别的
虽然如此,很多时候还是会产生很多没用的检查
我们需要模拟以下组件的核心函数
区别于上一种方法
这里的
每一个watcher封装着用于脏检查的函数
而watch方法就负责向watchers中添加watcher
它接受两个参数,一个取值函数watchExp和一个回调函数listener
digest方法会遍历整个watcher
last存储着上一个值,再通过取值函数获取值
通过比较可以知道值有没有变脏
如果脏了,就触发回调函数(渲染数据)并且更新last值
还要重新检查一遍watchers确保last和数据一致
(这里没有处理互相绑定死循环的问题,可以设置检查上限)
声明完组件类,我们就可以实例化一个组件
绑定节点,监听事件,还要手动进行脏检查
数据改变,触发setter渲染视图
视图影响数据没什么好说的,肯定需要监听input事件
这里我就写的简单点了
实际上vue实现的要比这复杂多得多
因为setter在很多情况下并不是万金油
也就是说并不是对象属性的任何变动它都能够监听的到
比如说以下场景:
向对象添加新属性
删除现有属性
数组改变
关于这些问题这里就不讨论了
如果有时间的同学可以去研究以下源码
此外还要说明一下
原本ES7草案中的Object.observe()由于严重的性能问题已经被移除了
==主页传送门==
什么是双向数据绑定呢?
简单的说
就是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与
$o的
data-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代码实现简单的双向数据绑定
- Vue数据双向绑定原理及简单实现方法
- Angular和Vue双向数据绑定的实现原理(重点是vue的双向绑定)
- 五十行javascript代码实现简单的双向数据绑定
- 五十行javascript代码实现简单的双向数据绑定
- 如何用javascript实现双向数据绑定 / Backbone.js简单入门范例
- 简单实现 angular1.x 双向数据绑定
- 应用defineProperty简单实现vue的双向数据绑定
- 五十行javascript代码实现简单的双向数据绑定
- Angular和Vue双向数据绑定的实现原理(重点是vue的双向绑定)
- javascript实现数据双向绑定的三种方式小结
- 实现非常简单的js双向数据绑定
- vue双向绑定的简单实现
- 用jquery实现的简单数据双向绑定
- angularjs中$http、$location、$watch及双向数据绑定学习实现简单登陆验证
- 关于vue双向绑定的简单实现
- javascript实现数据双向绑定的三种方式
- 实现非常简单的js双向数据绑定
- vue实现数据双向绑定的原理
- JavaScript数据绑定实现一个简单的 MVVM 库