JavaScript中双向数据绑定详解


Posted in Javascript onMay 03, 2017

双向数据绑定指的是将对象属性变化绑定到UI,或者反之。换句话说,如果我们有一个拥有name属性的user对象,当我们给user.name赋予一个新值是UI也会相应的显示新的名字。同样的,如果UI包括了一个输入字段用来输入用户名,输入一个新的值会导致user对象中的那么属性发生变化。

许多流行的客户端JavaScript框架例如Ember.js,AngularJS以及KnockoutJS都将双向数据绑定作为自己的头号特性。但是这并不意味着从零开始实现双向数据绑定就很困难,同样的当我们需要双向数据绑定时并不是只能够选择这些框架其中的一个。双向数据绑定底层的思想非常的基本,它可以被压缩成为三个步骤:

1.我们需要一个方法来识别哪个UI元素被绑定了相应的属性

2.我们需要监视属性和UI元素的变化

3.我们需要将所有变化传播到绑定的对象和元素

虽然实现的方法有很多,但是最简单也是最有效的途径是使用发布者-订阅者模式。思想很简单:我们可以使用自定义的data属性在HTML代码中指明绑定。所有绑定起来的JavaScript对象以及DOM元素都将“订阅”一个发布者对象。任何时候如果JavaScript对象或者一个HTML输入字段被侦测到发生了变化,我们将代理事件到发布者-订阅者模式,这会反过来将变化广播并传播到所有绑定的对象和元素。

使用jQuery的简单实现

使用jQuery来实现双向数据绑定非常的直接且简单,因为这个流行的库能够是我们轻松的订阅和发布DOM事件,以及我们自定义的事件:

function DataBinder(object_id){
 //使用一个jQuery对象作为简单的订阅者发布者
 var pubSub = jQuery({});

 //我们希望一个data元素可以在表单中指明绑定:data-bind-<object_id>="<property_name>"  

 var data_attr = "bind-" + object_id,
   message = object_id + ":change";

 //使用data-binding属性和代理来监听那个元素上的变化事件
 // 以便变化能够“广播”到所有的关联对象 

 jQuery(document).on("change","[data-" + data_attr + "]",function(evt){
  var input = jQuery(this);
  pubSub.trigger(message, [ $input.data(data_attr),$input.val()]);
 });

 //PubSub将变化传播到所有的绑定元素,设置input标签的值或者其他标签的HTML内容 

 pubSub.on(message,function(evt,prop_name,new_val){
  jQuery("[data-" + data_attr + "=" + prop_name + "]").each(function(){
  var $bound = jQuery(this);

  if($bound.is("input,text area,select")){
   $bound.val(new_val);
  }else{
   $bound.html(new_val);
  }
  });
 });

 return pubSub;
}

在这个实验中可以按照以下代码简单的实现一个User模型:

function User(uid){
 var binder = new DataBinder(uid),

  user = {
   atttibutes: {},

   //属性设置器使用数据绑定器PubSub来发布变化 

   set: function(attr_name,val){
    this.attriures[attr_name] = val;
    binder.trigger(uid + ":change", [attr_name, val, this]);
   },

   get: function(attr_name){
    return this.attributes[attr_name];
   },

   _binder: binder
  };

  binder.on(uid +":change",function(vet,attr_name,new_val,initiator){
   if(initiator !== user){
    user.set(attr_name,new_val);
   }
  })
}

现在,无论我们什么时候想把模型的属性绑定到UI的一部分上,我们只需要在相应的HTML元素上设置一个合适的data属性即可。

//JavaScript

var user = new User(123);
user.set("name","Wolfgang");

//html

<input type="number" data-bind-123="name" />

input字段的值会自动反映出user对象的name属性,反之亦然。任务完成了!

不使用jQuery来创建数据双向绑定

在入如今的大多数项目中,都可能已经用到了jQuery,因此完全可以借用前面的例子。但是如果我们更进一步,移除对jQuery的依赖会怎样呢?事实上,这并不是太困难(尤其是当我们限定只支持IE8以上的版本)。最终,我们需要使用原生的JavaScript来实现一个自定义的PubSub以及观察DOM事件。

function DataBinder(object_id){
 //创建一个简单地PubSub对象 

 var pubSub = {
  callbacks: {}.

  on: function(msg,calssback){
   this.callbacks[msg] = this.callbacks[msg] || [];
   this.callbacks[msg].push(callback);
  },

  publish: function(msg){
   this.callbacks[msg] = this.callbacks[msg] || [];
   for(var i = 0, len = this.callbacks[msg].length; i<lenli++){
    this.callbacks[msg][i].apply(this,arguments);
   }
  }
 },

 data_attr = "data-bind-" + object_id,
 message = object_id + ":change",

 changeHandler = function(evt){
  var target = evt.target || evt.srcElemnt, //IE8兼容
   prop_name = target.getAttribute(data_attr);

   if(prop_name && prop_name !== ""){
    pubSub.publish(message,prop_name,target.value);
   }
 };

 //监听变化事件并代理到PubSub 
 if(document.addEventListener){
  document.addEventListener("change",changeHandler,false);
 }else{
  //IE8使用attachEvent而不是addEventListener  
  document.attachEvent("onchange",changeHandler);
 }

 //PubSub将变化传播到所有绑定元素 

 pubSub.on(message,function(vet,prop_name,new)_val){
  var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"),
    tah_name;

  for(var i = 0,len =elements.length; i < len; i++){
   tag_name = elements[i].tagName.toLowerCase();

   if(tag_name === "input" || tag_name === "textarea" || tag_name === "select"){
   elements[i].value = new_val;
   }else{
    elements[i].innerHTML = new_val;
   }
  }
 });

 return pubSub;
}

模型可以和勤勉你的例子保持一直,除了在设置器中调用那个jQuery的trigger方法之外,它需要通过调用一个自定义的PubSub的publish方法来实现:

//在model的设置器中 

function User(uid){
//...

user = {
//...
set: function(attr_name,val){
 this.attribute[attr_name] = val;
 //使用“publish”方法 
 binder.publish(uid+ ":change", attr_name, val,this);
  }
 }

 //...
}

再一次,我们使用原生的JavaScript代码实现了相同的结果,而不是使用臃肿的JavaScript框架。

本文译自easy two way data-binding in JavaScript,原文地址http://www.lucaongaro.eu/blog/2012/12/02/easy-two-way-data-binding-in-javascript/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
下拉菜单点击实现连接跳转功能的js代码
May 19 Javascript
多种方法判断Javascript对象是否存在
Sep 22 Javascript
Express作者TJ告别Node.js奔向Go
Jul 14 Javascript
基于js实现投票的实例代码
Aug 04 Javascript
JS实现样式清新的横排下拉菜单效果
Oct 09 Javascript
基于BootStrap实现局部刷新分页实例代码
Aug 08 Javascript
原生js实现tab选项卡切换
Mar 23 Javascript
利用vue-router实现二级菜单内容转换
Nov 30 Javascript
webpack实现一个行内样式px转vw的loader示例
Sep 13 Javascript
详解JavaScript中的坐标和距离
May 27 Javascript
vue pages 多入口项目 + chainWebpack 全局引用缩写说明
Sep 21 Javascript
jQuery实现移动端扭蛋机抽奖
Nov 08 jQuery
Js实现中国公民身份证号码有效性验证实例代码
May 03 #Javascript
Vue原理剖析 实现双向绑定MVVM
May 03 #Javascript
利用node.js写一个爬取知乎妹纸图的小爬虫
May 03 #Javascript
Vue实现双向数据绑定
May 03 #Javascript
Angular 4.x 路由快速入门学习
May 03 #Javascript
javaScript 逻辑运算符使用技巧整理
May 03 #Javascript
浅谈Node.js轻量级Web框架Express4.x使用指南
May 03 #Javascript
You might like
PHP4 与 MySQL 数据库操作函数详解
2006/12/06 PHP
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
PHP图片自动裁切应付不同尺寸的显示
2014/10/16 PHP
php类的自动加载操作实例详解
2016/09/28 PHP
PHP二维数组实现去除重复项的方法【保留各个键值】
2017/12/21 PHP
CI框架网页缓存简单用法分析
2018/12/26 PHP
PHP 观察者模式深入理解与应用分析
2019/09/25 PHP
php实现推荐功能的简单实例
2019/09/29 PHP
基于jQuery的消息提示插件之旅 DivAlert(三)
2010/04/01 Javascript
jquery-syntax动态语法着色示例代码
2014/05/14 Javascript
使用focus方法让光标默认停留在INPUT框
2014/07/29 Javascript
js根据鼠标移动速度背景图片自动旋转的方法
2015/02/28 Javascript
解析javascript中鼠标滚轮事件
2015/05/26 Javascript
基于BootStrap Metronic开发框架经验小结【九】实现Web页面内容的打印预览和保存操作
2016/05/12 Javascript
JS正则表达式学习之贪婪和非贪婪模式实例总结
2016/12/26 Javascript
bootstrap导航栏、下拉菜单、表单的简单应用实例解析
2017/01/06 Javascript
利用Js+Css实现折纸动态导航效果实例源码
2017/01/25 Javascript
深入理解Nodejs Global 模块
2017/06/03 NodeJs
web前端页面生成exe可执行文件的方法
2018/02/08 Javascript
Vue开发之封装上传文件组件与用法示例
2019/04/25 Javascript
[06:10]6.81新信使新套装!给你一个炫酷的DOTA2
2014/05/06 DOTA
Python中几种导入模块的方式总结
2017/04/27 Python
Python 模拟购物车的实例讲解
2017/09/11 Python
Python Numpy:找到list中的np.nan值方法
2018/10/30 Python
pycharm修改file type方式
2019/11/19 Python
Python 爬虫批量爬取网页图片保存到本地的实现代码
2020/12/24 Python
瑞典Happy Socks美国官网:购买色彩斑斓的快乐袜子
2016/10/19 全球购物
Pat McGrath Labs官网:世界上最有影响力的化妆师推出的彩妆品牌
2018/01/07 全球购物
新秀丽官方旗舰店:Samsonite拉杆箱、双肩包、皮具
2018/03/05 全球购物
服务宗旨标语
2014/07/01 职场文书
英语课前三分钟演讲稿(6篇)
2014/09/13 职场文书
小学教育见习报告
2014/10/31 职场文书
2015年勤工助学工作总结
2015/04/29 职场文书
员工离职证明范本
2015/06/12 职场文书
python之PySide2安装使用及QT Designer UI设计案例教程
2021/07/26 Python
Golang入门之计时器
2022/05/04 Golang