详解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 相关文章推荐
使用javascript做的一个随机点名程序
Feb 13 Javascript
javascript设计模式之对象工厂函数与构造函数详解
Jul 30 Javascript
JS控制TreeView的结点选择
Nov 11 Javascript
jquery Banner轮播选项卡
Dec 26 Javascript
详解Jquery Easyui的验证扩展
Jan 09 Javascript
JavaScript获取URL参数的方法之一
Mar 24 Javascript
微信小程序之获取当前位置经纬度以及地图显示详解
May 09 Javascript
浅析node应用的timing-attack安全漏洞
Feb 28 Javascript
微信小程序实现红包功能(后端PHP实现逻辑)
Jul 11 Javascript
Vue2.0实现组件之间数据交互和通信操作示例
May 16 Javascript
vue axios post发送复杂对象问题
Jun 04 Javascript
JS前端轻量fabric.js系列物体基类
Aug 05 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
用PHP 快速生成 Flash 动画的方法
2007/03/06 PHP
Discuz 6.0+ 批量注册用户名
2009/09/13 PHP
PHP 读取和修改大文件的某行内容的代码
2009/10/30 PHP
Linux下快速搭建php开发环境
2017/03/13 PHP
Laravel框架在本地虚拟机快速安装的方法详解
2018/06/11 PHP
javascript实现的动态文字变换
2007/07/28 Javascript
Jquery 学习笔记(一)
2009/10/13 Javascript
JS构建页面的DOM节点结构的实现代码
2011/12/09 Javascript
Js实现滚动变色的文字效果
2014/06/16 Javascript
Nodejs学习笔记之Stream模块
2015/01/13 NodeJs
JS实现FLASH幻灯片图片切换效果的方法
2015/03/04 Javascript
基于JavaScript实现动态添加删除表格的行
2016/02/01 Javascript
jQuery+PHP实现微信转盘抽奖功能的方法
2016/05/25 Javascript
jQuery中常用动画效果函数(日常整理)
2016/09/17 Javascript
js实现选项卡内容切换以及折叠和展开效果【推荐】
2017/01/08 Javascript
将angular-ui的分页组件封装成指令的方法详解
2017/05/10 Javascript
vue中如何实现变量和字符串拼接
2017/06/19 Javascript
JS/jQuery实现DIV延时几秒后消失或显示的方法
2018/02/12 jQuery
使用express来代理服务的方法
2019/06/21 Javascript
vue 指令和过滤器的基本使用(品牌管理案例)
2019/11/04 Javascript
vue和H5 draggable实现拖拽并替换效果
2020/07/29 Javascript
Vue中computed及watch区别实例解析
2020/08/01 Javascript
[02:51]2018年度DOTA2最佳中单位选手-完美盛典
2018/12/17 DOTA
Python通过websocket与js客户端通信示例分析
2014/06/25 Python
详尽讲述用Python的Django框架测试驱动开发的教程
2015/04/22 Python
浅析Python 中几种字符串格式化方法及其比较
2019/07/02 Python
Python使用configparser库读取配置文件
2020/02/22 Python
详解前端HTML5几种存储方式的总结
2016/12/27 HTML / CSS
世界上最大的铁人三项商店:Tri UK
2020/11/04 全球购物
递归实现回文判断(如:abcdedbca就是回文,判断一个面试者对递归理解的简单程序)
2013/04/28 面试题
社会实践活动总结范文
2014/07/03 职场文书
酒店管理失职检讨书
2014/09/16 职场文书
博士生专家推荐信
2015/03/25 职场文书
营运督导岗位职责
2015/04/10 职场文书
高二语文教学反思
2016/02/16 职场文书
JavaScript架构localStorage特殊场景下二次封装操作
2022/06/21 Javascript