详解vue的数据binding绑定原理


Posted in Javascript onApril 12, 2017

自从angular火了以后,各种mvc框架喷涌而出,angular虽然比较火,但是他的坑还是蛮多的,还有许多性能问题被人们吐槽。比如坑爹的脏检查机制,数据binding是受人喜爱的,脏检查就有点…性能低下了。有时候改了一个地方,脏循环要循环多次来保证数据是不是真的变了和是否停止变化了。这样性能就很低了。于是人们开始钻研新的双向数据binding的方法。尤大的vue binding就是本人蛮喜欢的一种实现方式,本文跟随尤大的一个例子来详解vue的数据binding的原理。

数据binding,一般也就是正则匹配到元素中的模板,然后代码切换为程序员给的data。双向binding除了脏检查机制,尤大用es5的defineProperty来实现的双向数据binding,拦截了对象的set和get方法,这个就比较有效了。同样的avalon也是此方法,用正美的话说:“我只是在var data = 1的时候拦截了'=‘“。原理都是一样的。我们直接上例子:(这里跟随尤大的脚步)(复制可用)

<!DOCTYPE html>
<html>
  <head>
    <title>ideal</title>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="test">
      <p>{{msg}}</p>
      <p>{{msg}}</p>
      <p>{{msg}}</p>
      <p>{{what}}</p>
      <p>{{hey}}</p>
    </div>
    <script>
      var bindingMark = 'data-element-binding'
      function Element (id, initData) {
        var self   = this,
          el     = self.el = document.getElementById(id)
          bindings = {} //内部暂存绑定数据及dom
          data   = self.data = {} //存储bingding数据并实现监控
          content = el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken)
          el.innerHTML = content
         
        for (var variable in bindings) {
          bind(variable); //将每个数据的名称比如'msg'绑定到data
        }
        if (initData) {
          for (var variable in initData) {
            data[variable] = initData[variable]
          }
        }
        function markToken (match, variable) {
          bindings[variable] = {} //bindings里存储了数据来源的字段比如bindings['msg']
          return '<span ' + bindingMark + '="' + variable +'"></span>'
        }
        function bind (variable) {
          bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]')//bindings里再存储msg绑定的元素
          ;[].forEach.call(bindings[variable].els, function (e) { //删除data-element-binding属性
            e.removeAttribute(bindingMark)
          })
          Object.defineProperty(data, variable, { //es5观察属性
            set: function (newVal) {
              [].forEach.call(bindings[variable].els, function (e) {
                bindings[variable].value = e.textContent = newVal //=>这里才是实现的绑定,更新数据到dom并更新内部暂存数据
              })
            },
            get: function () {
              return bindings[variable].value //取数据仅仅是内部暂存的数据
            }
          })
        }
      }
      
      var app = new Element('test', {
        msg: 'hello',
        what: 'hi'
      })

    </script>
  </body>
</html>

这应该就是vue数据binding的原理了。一些地方都写在注释了。

这个数据binding的流程是怎样的?

开始弄一个属性占位符data-element-binding,正则把原元素的内容加个标签再加上此属性,属性的值是绑定的值的key,比如msg是hello,则data-element-binding=”msg“,同时在匹配的时候把这个key存到bindings对象里面,bindings暂存绑定数据。

遍历bindings对象,再把每个存在data-element-binding的元素以它的key(比如msg)存到bindings里面,然后删除dom中的data-element-binding属性。

最重要的是:它维护了一个data对象,这个data对象是binding的关键,他是m和v的接口。他循环定义属性key(比如msg),然后在set的时候更新dom的数据,完成的binding,也就是劫持了”=“的操作,并且把这个值存到bindings里暂存。set的时候返回的是bindings里面的暂存数据。这样的好处就是我始终是使用js的原生方法,我改变data就会实现双向的绑定。v=>m的binding只需要一个onchange事件就可以,避免了循环的检查。

vue的单项绑定就是这样了,有趣的地方是你在控制台修改data的属性,p标签的内容也会相应改变,这正是es5 set方法的特点,这样的双向绑定是好维护并且没有副作用的,而且性能还是很强大的。

那见识过vue的数据绑定以后我们实现一个input的双向数据绑定(跟源码不一样的说)

其实input的双向binding很简单,因为vue已经帮我们做好了data属性,那么我在操作data的时候实际上已经完成了m到v的双向绑定。那么原理很简单,onkey的时候就修改data数据就好了。先上代码:(复制可用)

<!DOCTYPE html>
<html>
  <head>
    <title>ideal</title>
    <meta charset="utf-8">
  </head>
  <body>
    <input class="test" type="text" name="asd" onkeyup ="handleChange()" v-model="hey">
    <input class="test" type="" name="" onkeyup ="handleChange()" v-model="msg">
    <script>
      var bindingMark = 'data-element-binding'
      function Element (classa, initData) {
        var self   = this,
          el     = self.el = document.getElementsByClassName(classa),//多个input改为class
          bindings = {} 
          data   = self.data = {} 
          for (var i = 0; i < el.length; i++) {
            content = el[i].outerHTML.replace(/v-model=\"(.*)\"/g, markToken);
            el[i].outerHTML = content
          }
        for (var variable in bindings) {
          bind(variable); 
        }
        if (initData) {
          for (var variable in initData) {
            data[variable] = initData[variable]
          }
        }
        function markToken (match, variable) {
          bindings[variable] = {} 
          return bindingMark + '="' + variable +'"' //内填一个span变为只改它的元素
        }
        function bind (variable) {

          bindings[variable].els = document.querySelectorAll('[' + bindingMark + '="' + variable + '"]')//document获取binding元素
          ;
          Object.defineProperty(data, variable, { 
            set: function (newVal) {
              [].forEach.call(bindings[variable].els, function (e) {
                bindings[variable].value = e.value = newVal //=>textContent改为input的value
              })
            },
            get: function () {
              return bindings[variable].value 
            }
          })
        }
      }
      
      var app = new Element('test', {
        msg: 'hello',
        hey:'aaa'
      })
      function handleChange(e){ //增加v=>m的绑定
        e = e || window.event
        var key = e.target.outerHTML.match(/data-element-binding=\"(.*)\"/)[1];
        data[key] = e.target.value
        console.log(data.hey,data.msg);
      }
    </script>
  </body>
</html>

修改的地方也注释了,结果会看到当我打开的时候,input已经被binding了msg和hey:

详解vue的数据binding绑定原理

当我有输入的时候:

详解vue的数据binding绑定原理

当我操作data的时候:

详解vue的数据binding绑定原理

双向binding已经ok,么么哒。

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

Javascript 相关文章推荐
验证用户是否修改过页面的数据的实现方法
Sep 26 Javascript
js控制的回到页面顶端goTop的代码实现
Mar 20 Javascript
JavaScript动态创建div属性和样式示例代码
Oct 09 Javascript
jquery制作搜狐快站页面效果示例分享
Feb 21 Javascript
Css3制作变形与动画效果
Jul 24 Javascript
基于javascript数组实现图片轮播
May 02 Javascript
webpack4.0打包优化策略整理小结
Mar 30 Javascript
js实现敏感词过滤算法及实现逻辑
Jul 24 Javascript
axios简单实现小程序延时loading指示
Jul 30 Javascript
JS返回页面时自动回滚到历史浏览位置
Sep 26 Javascript
详解element-ui设置下拉选择切换必填和非必填
Jun 17 Javascript
ReactRouter的实现方法
Jan 25 Javascript
angular学习之ngRoute路由机制
Apr 12 #Javascript
Node.js发送HTTP客户端请求并显示响应结果的方法示例
Apr 12 #Javascript
微信小程序登录态控制深入分析
Apr 12 #Javascript
JavaScript数据结构之二叉查找树的定义与表示方法
Apr 12 #Javascript
微信小程序微信支付接入开发实例详解
Apr 12 #Javascript
JavaScript数据结构之广义表的定义与表示方法详解
Apr 12 #Javascript
JavaScript数据结构之数组的表示方法示例
Apr 12 #Javascript
You might like
php5.3 废弃函数小结
2010/05/16 PHP
php无限分类且支持输出树状图的详细介绍
2013/06/19 PHP
解析PHP中数组元素升序、降序以及重新排序的函数
2013/06/20 PHP
php使用pdo连接并查询sql数据库的方法
2014/12/24 PHP
WampServer搭建php环境时遇到的问题汇总
2015/07/23 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
PHP使用redis位图bitMap 实现签到功能
2019/10/08 PHP
Javascript----文件操作
2007/01/18 Javascript
jQuery 使用手册(三)
2009/09/23 Javascript
FusionCharts图表显示双Y轴双(多)曲线
2012/11/22 Javascript
JavaScript1.6数组新特性介绍以及JQuery的几个工具方法
2013/12/06 Javascript
浅谈javascript六种数据类型以及特殊注意点
2013/12/20 Javascript
json属性名为什么要双引号(个人猜测)
2014/07/31 Javascript
javascript学习笔记(四)function函数部分
2014/09/30 Javascript
纯js实现无限空间大小的本地存储
2015/06/18 Javascript
Vue使用json-server进行后端数据模拟功能
2018/04/17 Javascript
vue返回上一页面时回到原先滚动的位置的方法
2018/12/20 Javascript
利用Bootstrap Multiselect实现下拉框多选功能
2019/04/08 Javascript
js中的this的指向问题详解
2019/08/29 Javascript
JavaScript 实现同时选取多个时间段的方法
2019/10/17 Javascript
通过javascript实现扫雷游戏代码实例
2020/02/09 Javascript
vue设置全局访问接口API地址操作
2020/08/14 Javascript
[01:11:48]Fnatic vs IG 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/18 DOTA
对Python 文件夹遍历和文件查找的实例讲解
2018/04/26 Python
python 用for循环实现1~n求和的实例
2019/02/01 Python
python实现五子棋游戏
2019/06/18 Python
基于Python函数和变量名解析
2019/07/19 Python
python 实现Harris角点检测算法
2020/12/11 Python
详解如何将 Canvas 绘制过程转为视频
2021/01/25 HTML / CSS
美体小铺加拿大官方网站:The Body Shop加拿大
2016/10/30 全球购物
英国水族馆和池塘用品购物网站:Warehouse Aquatics
2019/08/29 全球购物
春节晚会主持词
2014/03/24 职场文书
2016年小学“我们的节日·中秋节”活动总结
2016/04/05 职场文书
2019关于实习生工作安排及待遇的管理方案!
2019/07/16 职场文书
Python实战之用tkinter库做一个鼠标模拟点击器
2021/04/27 Python
Python图片检索之以图搜图
2021/05/31 Python