详解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 鼠标滚轮事件
Apr 09 Javascript
如何通过javascript操作web控件的自定义属性
Nov 25 Javascript
JS、CSS以及img对DOMContentLoaded事件的影响
Aug 12 Javascript
Javascript实现禁止输入中文或英文的例子
Dec 09 Javascript
jQuery仿Flash上下翻动的中英文导航菜单实例
Mar 10 Javascript
jquery制作多功能轮播图插件
Apr 02 Javascript
【JS+CSS3】实现带预览图幻灯片效果的示例代码
Mar 17 Javascript
jQuery实现web页面樱花坠落的特效
Jun 01 jQuery
详解vuejs之v-for列表渲染
Jun 22 Javascript
基于iview的router常用控制方式
May 30 Javascript
javaScript代码飘红报错看不懂?读完这篇文章再试试
Aug 19 Javascript
Vue 数据响应式相关总结
Jan 28 Vue.js
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 session有效期问题
2009/04/26 PHP
php入门学习知识点四 PHP正则表达式基本应用
2011/07/14 PHP
PHP数组实例总结与说明
2011/08/23 PHP
php 文本文件的读取效率
2012/02/10 PHP
ThinkPHP模版引擎之变量输出详解
2014/12/05 PHP
通过Email发送PHP错误的方法
2015/07/20 PHP
php+javascript实现的动态显示服务器运行程序进度条功能示例
2017/08/07 PHP
js下用eval生成JSON对象
2010/09/17 Javascript
网页实时显示服务器时间和javscript自运行时钟
2014/06/09 Javascript
JavaScript面试开发常用的知识点总结
2016/08/08 Javascript
jQuery实现表格元素动态创建功能
2017/01/09 Javascript
js仿微信公众平台打标签功能
2017/04/08 Javascript
Three.js利用顶点绘制立方体的方法详解
2017/09/27 Javascript
详解Vue-cli代理解决跨域问题
2017/09/27 Javascript
ES6关于Promise的用法详解
2018/05/07 Javascript
微信小程序实现topBar底部选择栏效果
2018/07/20 Javascript
[02:09:59]火猫TV国士无双dota2 6.82版本详解(下)
2014/09/29 DOTA
python实现查询苹果手机维修进度
2015/03/16 Python
Python and、or以及and-or语法总结
2015/04/14 Python
Python循环语句之break与continue的用法
2015/10/14 Python
Tornado Web Server框架编写简易Python服务器
2018/07/28 Python
pandas 对group进行聚合的例子
2019/12/27 Python
Python OpenCV实现测量图片物体宽度
2020/05/27 Python
Python SMTP配置参数并发送邮件
2020/06/16 Python
全球精选男装和家居用品:Article
2020/04/13 全球购物
屈臣氏泰国官网:Watsons TH
2021/02/23 全球购物
法律专业应届生自荐信范文
2014/01/06 职场文书
创建绿色学校先进个人材料
2014/08/20 职场文书
2014财务年度工作总结
2014/11/11 职场文书
说谎欺骗人检讨书300字
2014/11/18 职场文书
2015年学校食堂工作总结
2015/04/22 职场文书
2016年“节能宣传周”活动总结
2016/04/05 职场文书
聊聊pytorch测试的时候为何要加上model.eval()
2021/05/23 Python
JS代码编译器Monaco使用方法
2021/06/11 Javascript
Python中的程序流程控制语句
2022/02/24 Python
如何让你的Nginx支持分布式追踪详解
2022/07/07 Servers