技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> JavaScript --> javascript双向数据绑定

javascript双向数据绑定

浏览:2204次  出处信息

   双向数据绑定是AngularJs重要特性之一。双向数据绑定就是指用户在视图上的修改会自动同步到数据模型中去;同样的,如果数据模型中的值发生了变化,也会立刻同步到视图中去。显而易见,双向数据绑定最大的优点就是无需进行和单向数据绑定的那些CRUD(Create,Retrieve,Update,Delete)操作。当然,数据双向绑定也不是万能的,像游戏,图形界面编辑器,这种DOM操作很频繁也很复杂的应用,和CRUD应用就有很大的不同,数据双向绑定在这些应用中就无法发挥效用了。双向数据绑定思想,以前在WPF技术中就接触过,这两天借助灵活的JS了解一下其实现原理。

   two-way-binding

Angular双向绑定简单实例

   该实例来自Angular中文网

<!DOCTYPE html>  
<html ng-app>  
<head>  
  <meta charset="UTF-8">
  <title>angular test</title>
  <script src="lib/angular.min.js"></script>
</head>  
<body>  
  Your name: <input type="text" ng-model="yourname" placeholder="World">
  <hr>
  Hello {{ yourname || 'World' }}!
</body>  
</html>  

   现在试着在输入框中键入您的名称,您键入的名称将立即更新显示在问候语中。 这就是AngularJS双向数据绑定的概念。 输入框的任何更改会立即反映到模型变量(一个方向),模型变量的任何更改都会立即反映到问候语文本中(另一方向)。

双向数据绑定实现原理

   双向数据绑定底层的思想其实非常的基本,它可以被压缩成为三个步骤:

  • 识别哪个UI元素被绑定了相应的属性  

  • 监视属性和UI元素的变化  

  • 将所有变化传播到绑定的对象和元素

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

       jQuery实现

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

    See the Pen two-way-data-binding by chenjunxyf (@chenjunxyf) on CodePen.

       神奇吧,我们在文本框中输入文字,后面的内容页跟着变化,实现了Angular一样的功能效果!

       原生javascript实现

       jQuery实现比较简单,因为它帮我们解决了很多底层的事情。如果,我们不用jQuery,依然可以很容易的实现双向数据绑定功能,当然在不考虑老本版的IE情况下!嘿嘿~

       观察者模式+DOM事件监听实现双向绑定核心功能:

    /*
    * DataBinder by javascript
    *
    * @param {object_id} String
    */
    function DataBinder(object_id) {  
      // 创建一个简单的pubSub对象
      var pubSub = {
        callbacks: {},
        on: function(msg, callback) {
          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 < len; i++) {
            this.callbacks[msg][i].apply(this, arguments);
          }
        }
      },
    
      data_attr = "data-bind-" + object_id,
      message = object_id + ":change",
    
      changeHandler = function(event) {
        // IE8兼容
        var target = event.target || event.srcElement,
              prop_name = target.getAttribute(data_attr);
        if (prop_name && prop_name !== "") {
          pubSub.publish(message, prop_name, target.value);
        }
      };
    
      // 监听事件变化,并代理到pubSub
      if (document.addEventListener) {
        document.addEventListener("keyup", changeHandler, false);
      } else {
        // 兼容IE8
        document.attachEvent("onkeyup", changeHandler);
      }
      // 界面修改变化
      pubSub.on(message, function(event, prop_name, new_val) {
        var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"),
              tag_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;
    }
    

       简单的user模型:

    /*
    * User Model
    *
    * @param {uid} String
    */
    function User(uid) {  
      var binder = new DataBinder(uid);
    
      var user = {
        attribute: {},
    
        // 属性设置器使用数据绑定器pubSub来发布
        set: function(attr_name, val) {
          this.attribute[attr_name] = val;
          binder.publish(uid + ":change", attr_name, val, this);
        },
    
        get: function(attr_name) {
          return this.attribute[attr_name];
        },
    
        _binder: binder
      };
    
      // Model修改变化
      binder.on(uid + ":change", function(event, attr_name, new_val, initiator) {
        if (initiator !== user) {
          user.set(attr_name, new_val);
        }
      });
    
      return user;
    }
    

       用以上代码代替jQuery实现版本的js代码,会发现实现了同样的双向数据绑定效果!

    结语

       观察者模式只是双向数据绑定实现的一种方法,该方法比较容易理解,很多场景下效率也不错。AngularJs利用“脏读”实现了双向数据绑定,“脏读”并不像观察者模式那样能够实时得到数据变化通知,然后进行更新操作。“脏读”由指定事件触发,确定有数据变化了,然后再进行界面修改。“脏读” 会从rootscope开始遍历, 检查所有的watcher,这就是其性能比较低的原因!两种实现方式,都各有优势,具体应用时应考虑场景的需求。

    参考

  •    谈谈JavaScript中的双向数据绑定

  •    Angular沉思录(一) 数据的双向绑定

  •    AngularJS的数据双向绑定是怎么实现的?

  •    聊一聊web开发中的双向数据绑定吧

建议继续学习:

  1. 关于Python的闭包和后期绑定    (阅读:1872)
  2. zepto/jQuery、AngularJS、React、Nuclear的演化    (阅读:1234)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:javascript依赖注入
后一篇:用 JS 复制艺术 >>
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1