详解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 学习笔记(十三)Dom创建表格
Jan 21 Javascript
Javascript异步表单提交,图片上传,兼容异步模拟ajax技术
May 10 Javascript
JS控件ASP.NET的treeview控件全选或者取消(示例代码)
Dec 16 Javascript
原生javascript实现隔行换色
Jan 04 Javascript
JavaScript插件化开发教程 (二)
Jan 27 Javascript
js实现图片从左往右渐变切换效果的方法
Feb 06 Javascript
理解Angular数据双向绑定
Jan 10 Javascript
原生javascript实现分享到朋友圈功能 支持ios和android
May 11 Javascript
jQuery EasyUI ProgressBar进度条组件
Feb 28 Javascript
详解vuejs2.0 select 动态绑定下拉框支持多选
Apr 25 Javascript
javascript中call,apply,bind的区别详解
Dec 11 Javascript
JavaScript 去重和重复次数统计
Mar 31 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取余函数介绍MOD(x,y)与x%y
2014/05/15 PHP
PHP中递归的实现实例详解
2017/11/14 PHP
PHP实现动态添加XML中数据的方法
2018/03/30 PHP
PHP实现提取多维数组指定一列的方法总结
2019/12/04 PHP
ASP.NET jQuery 实例14 在ASP.NET form中校验时间范围
2012/02/03 Javascript
jQuery验证元素是否为空的两种常用方法
2015/03/17 Javascript
jQuery实现拖拽页面元素并将其保存到cookie的方法
2016/06/12 Javascript
基于Bootstrap的Metronic框架实现页面链接收藏夹功能
2016/08/29 Javascript
超详细的JS弹出窗口代码大全
2020/04/18 Javascript
js学习总结_基于数据类型检测的四种方式(必看)
2017/07/04 Javascript
详解webpack 打包文件体积过大解决方案(code splitting)
2018/04/10 Javascript
vue中子组件向父组件传递数据的实例代码(实现加减功能)
2018/04/20 Javascript
基于React+Redux的SSR实现方法
2018/07/03 Javascript
微信小程序传值以及获取值方法的详解
2019/04/29 Javascript
基于layui实现高级搜索(筛选)功能
2019/07/26 Javascript
[04:04]显微镜下的DOTA2第六期——电影级别的华丽团战
2014/06/20 DOTA
[01:03]PWL开团时刻DAY6——别打我
2020/11/05 DOTA
[45:44]完美世界DOTA2联赛PWL S2 FTD vs PXG 第一场 11.27
2020/12/01 DOTA
在Python中使用HTML模版的教程
2015/04/29 Python
Python常用知识点汇总
2016/05/08 Python
python字典键值对的添加和遍历方法
2016/09/11 Python
Python数据结构之翻转链表
2017/02/25 Python
python 全文检索引擎详解
2017/04/25 Python
Numpy 改变数组维度的几种方法小结
2018/08/02 Python
python分块读取大数据,避免内存不足的方法
2018/12/10 Python
python 执行终端/控制台命令的例子
2019/07/12 Python
python 识别登录验证码图片功能的实现代码(完整代码)
2020/07/03 Python
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
美国在线工具商店:Acme Tools
2018/06/26 全球购物
实验教师岗位职责
2014/02/13 职场文书
社区工作感言
2014/02/21 职场文书
合作意向书格式及范文
2014/03/31 职场文书
清正廉洁演讲稿
2014/05/22 职场文书
2014旅游局党组书记党建工作汇报材料
2014/11/02 职场文书
商场收银员岗位职责
2015/04/07 职场文书
八年级作文之感恩
2019/11/22 职场文书