一入前端深似海,从此红尘是路人系列第九弹之如何实现一个双向数据绑定

前言

简单介绍一下双向数据绑定实现的是一个什么样的东西。首先有两个东西,他们分别是:
V-视图层
M-数据层
1、视图层传向数据层:V发生改变,M也发生改变
2、数据层传向视图层:M发生改变,V也发生改变
那么接下来我们也将自己书写代码实现这样两种功能,从而实现双向数据绑定。最终实现的效果如图:
一入前端深似海,从此红尘是路人系列第九弹之如何实现一个双向数据绑定
一入前端深似海,从此红尘是路人系列第九弹之如何实现一个双向数据绑定
修改input框里面的内容,p标签内容也实时相对应发生改变,data里面的数据也会发生改变
一入前端深似海,从此红尘是路人系列第九弹之如何实现一个双向数据绑定

一、先奉献上自己完整的代码

这里我是基于jQuery进行的代码编写,其中的方法将在下面进行详细的解析


;(function($,doc,win) {
var VM = function(opt) {
  this.setting = {};
  $.extend(this.setting, opt.data);

  // 初始化VM
  this.init();
};
VM.prototype = {
  init: function() {
    this.render('input');
    this._render('p');
  },
  render: function(dom) {
    var self = this
      , data = self.setting;
    $(dom).each(function() {
      var _attr = $(this).attr('xq-model');
      if (_attr !== undefined) {
        if (data[_attr] !== undefined) {
          $(this).attr('value', data[_attr]);
          self.inputChange($(this), _attr);
        }
      }
    });
  },
  _render: function(dom) {
    var self = this;
    $(dom).each(function() {
      var val = $(this).html() || $(this).text() || $(this).val();
      if (val.indexOf('}}') !== -1 && val.indexOf('}}') !== -1) {
        val = val.replace(/^s+|{{|s+/,'');
        val = val.replace(/}}|[\s+]/g,'');
        self.labelChange(this, val);
      }
    })
  },
  labelRender: function(dom, _attr) {
    var self = this
      , data = self.setting;
    $(dom).each(function() {
      var val = $(this).attr(_attr);
      if (val !== undefined) {
        if (data[_attr] !== undefined) {
          $(this).html(data[_attr]) || $(this).text(data[_attr]) || $(this).val(data[_attr]);
        }
      }
    })
  },
  inputChange: function(_this, _attr) {
    var self = this
      , data = self.setting;
    _this.unbind('keydown');
    _this.keydown(function(){
      _this.unbind('keyup');
      _this.keyup(function() {
        var changeVal = _this.val()
          , oldVal = data[_attr];
        data[_attr] = changeVal;
        self.render('input');
        self.labelRender('p', _attr);
      })
    })
  },
  labelChange: function(_this,val) {
    var self = this
      , data = self.setting;
    if (val !== undefined) {
      if (data[val] !== undefined) {
        $(_this).html(data[val]) || $(_this).text(data[val]) || $(_this).val(data[val]);
      }
    }
    $(_this).attr(val,'')
  }
};
window.VM = VM;
})(jQuery,document,window);

二、创建自己的绑定规则

这里关于指令,我没有做作用域的包裹,比较完善的是需要用一个controller指令讲需要展示的view视图层包裹起来的。如果有想法的小伙伴们可以在最后自行去思考,接下来先直接上html代码


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>双向数据绑定</title>
</head>
<body>
    <input type="text" xq-model="msg">
    <p>
      {{ msg }}
    </p>
    <input type="text" xq-model="my">
    <p>{{ my }}</p>
</body>
<script src="js/jquery.js"></script>
<script src="js/api.js"></script>
</html>

这里我制定的双向指令是xq-model,内容的解析规则是{{}}。大家有自己喜欢的指令可以自行制定。

三、实现自己的绑定规则

我们现在需要做的就是一步一步去实现我们自己规定的一些绑定规则

首先我们需要做的第一步就是渲染出input中的xq-model指令指定的数据


/**
 * [render input框渲染]
 * @param  {[type]} dom [input]
 */
render: function(dom) {
  var self = this
    , data = self.setting;
  // 遍历dom中所有的input,搜寻带xq-model指令的input
  $(dom).each(function() {
    var _attr = $(this).attr('xq-model');
    // 判断是否有对应绑定data数据
    if (_attr !== undefined) {
      if (data[_attr] !== undefined) {
        $(this).attr('value', data[_attr]);
        // 调用input值改变方法
        self.inputChange($(this), _attr);
      }
    }
  });
}

实现inputChange方法


/**
 * [inputChange 值改变]
 * @param  {[type]} _this [当前input]
 * @param  {[type]} _attr [当前绑定对应的data数据]
 */
inputChange: function(_this, _attr) {
  var self = this
    , data = self.setting;
  // 绑定键盘事件进行监听
  _this.unbind('keydown');
  _this.keydown(function(){
    _this.unbind('keyup');
    _this.keyup(function() {
      var changeVal = _this.val()
        , oldVal = data[_attr];
      data[_attr] = changeVal;
      // 调用input框渲染方法
      self.render('input');
      // 调用标签值改变渲染方法
      self.labelRender('p', _attr);
    })
  })
}

到这里,对于input框的渲染及data数据的双向算完成了第一个了,接下来我们需要实现的便是对于标签的渲染


/**
 * [_render 标签渲染]
 * @param  {[type]} dom [element]
 */
_render: function(dom) {
  var self = this;
  // 遍历标签,拿到里面的内容
  $(dom).each(function() {
    var val = $(this).html() || $(this).text() || $(this).val();
    // 判断是否存在{{}},并截取拿到{{}} 里面的内容
    if (val.indexOf('}}') !== -1 && val.indexOf('}}') !== -1) {
      val = val.replace(/^s+|{{|s+/,'');
      val = val.replace(/}}|[\s+]/g,'');
      // 调用标签赋值方法
      self.labelChange(this, val);
    }
  })
}

实现labelChange方法


/**
 * [inputChange 值改变]
 * @param  {[type]} _this [当前input]
 * @param  {[type]} _attr [当前绑定对应的data数据]
 */
labelChange: function(_this,val) {
  var self = this
    , data = self.setting;
  if (val !== undefined) {
    if (data[val] !== undefined) {
      $(_this).html(data[val]) || $(_this).text(data[val]) || $(_this).val(data[val]);
    }
  }
  // 给当前对象绑定一个属性对应其data数据
  $(_this).attr(val,'')
}

最后实现input框输入,实时修改对应的标签的值labelRender方法


/**
 * [labelRender 标签实时渲染]
 * @param  {[type]} dom   [element]
 * @param  {[type]} _attr [当前data数据的key]
 */
labelRender: function(dom, _attr) {
  var self = this
    , data = self.setting;
  $(dom).each(function() {
    // 通过labelChange获取到对应data数据的attr属性
    var val = $(this).attr(_attr);
    if (val !== undefined) {
      if (data[_attr] !== undefined) {
        $(this).html(data[_attr]) || $(this).text(data[_attr]) || $(this).val(data[_attr]);
      }
    }
  })
}

最后我们调用一下实现好的VM


$(function() {
  new VM({
    data: {
      msg: 'hello',
      my: 'hehe'
    }
  });
})

好了,属于我们自己的一个简单的双向数据绑定至此便算是完成了,大家也可以自己将这个进行不断的完善,自己去实现一个完整的双向数据绑定。小伙伴们,加油↖(^ω^)↗

原文发布时间为:2016年11月21日
原文作者:qiangdada 

本文来源:开源中国 如需转载请联系原作者


上一篇:PolarDB-X 1.0-API参考-1.0(2019版本)-其他-DescribeInstanceSwitchAzone


下一篇:《深入浅出DPDK》—第2章2.8节时代背景