详解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在IE中“意外地调用了方法或属性访问”
Nov 19 Javascript
统计jQuery中各字符串出现次数的工具
May 03 Javascript
js同比例缩放图片的小例子
Oct 30 Javascript
JS烟花背景效果实现方法
Mar 03 Javascript
JavaScript实现的简单烟花特效代码
Oct 20 Javascript
jquery滚动条插件(可以自定义)
Dec 11 Javascript
JS定时检测任务任务完成后执行下一步的解决办法
Dec 22 Javascript
JavaScript字符串_动力节点Java学院整理
Jun 27 Javascript
详解Vue调用手机相机和相册以及上传
May 05 Javascript
react基本安装与测试示例
Apr 27 Javascript
vue实例的选项总结
Jun 09 Javascript
JavaScript 与 TypeScript之间的联系
Nov 27 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
图书管理程序(一)
2006/10/09 PHP
PHP 如何向 MySQL 发送数据
2006/10/09 PHP
php远程下载类分享
2016/04/13 PHP
jquery文字上下滚动的实现方法
2013/03/22 Javascript
JS数组的常见用法实例
2015/02/10 Javascript
javascript合并表格单元格实例代码
2016/01/03 Javascript
神奇!js+CSS+DIV实现文字颜色渐变效果
2016/03/16 Javascript
javascript实现文字无缝滚动
2016/12/27 Javascript
原生js实现对Ajax的封装(仿jquery)
2017/01/22 Javascript
JS中去掉array中重复元素的方法
2017/05/26 Javascript
详解webpack进阶之插件篇
2017/07/06 Javascript
Vue.js移动端左滑删除组件的实现代码
2017/09/08 Javascript
Vue数据双向绑定原理及简单实现方法
2018/05/18 Javascript
详解Vue CLI3配置之filenameHashing使用和源码设计使用和源码设计
2018/08/31 Javascript
JS实现炫酷轮播图
2020/11/15 Javascript
[02:11]2016国际邀请赛中国区预选赛全程回顾
2016/07/01 DOTA
Python中使用PyHook监听鼠标和键盘事件实例
2014/07/18 Python
python中正则表达式的使用详解
2014/10/17 Python
利用Python中的pandas库对cdn日志进行分析详解
2017/03/07 Python
Python 爬虫之Beautiful Soup模块使用指南
2018/07/05 Python
Python实现模拟浏览器请求及会话保持操作示例
2018/07/30 Python
python中的不可变数据类型与可变数据类型详解
2018/09/16 Python
python networkx 包绘制复杂网络关系图的实现
2019/07/10 Python
关于Kotlin中SAM转换的那些事
2020/09/15 Python
Python Spyder 调出缩进对齐线的操作
2021/02/26 Python
HTML5 Web存储方式的localStorage和sessionStorage进行数据本地存储案例应用
2012/12/09 HTML / CSS
优秀员工年终发言演讲稿
2014/01/01 职场文书
酒店工程部经理岗位职责
2015/04/09 职场文书
2015年初一班主任工作总结
2015/05/13 职场文书
二婚主持词
2015/06/30 职场文书
六五普法先进个人主要事迹材料
2015/11/03 职场文书
详解vue中v-for的key唯一性
2021/05/15 Vue.js
python基于tkinter实现gif录屏功能
2021/05/19 Python
Python时间操作之pytz模块使用详解
2022/06/14 Python
详解CSS中postion和opacity及cursor的特性
2022/08/14 HTML / CSS
源码安装apache脚本部署过程详解
2022/09/23 Servers