前端的三种数据绑定技术

开发 前端
本文写了以下三种不同方式的数据绑定(只实现了单向绑定)。

 本喵写了以下三种不同方式的数据绑定(只实现了单向绑定):

  • 第一种,使用了“脏值”检测,该方法是 angular 的数据绑定原理。
  • 第二种,使用了 es5 的 Object.defineProperty(),vue2 的数据绑定就是基于该方法。
  • 第三种,使用了 es6 的 Proxy ,vue3 的数据绑定开始全盘换为这种方式。

废话不多说,直接撸起代码~

[[360473]]

01 脏值检测

如果绑定的数据过多,脏值检测可能会造成性能问题,因为每次改变值,都需要进行轮询改变对应的值。

  1. <!DOCTYPE html> 
  2. <html lang="zh-CN"
  3.  
  4. <head> 
  5.   <meta charset="UTF-8"
  6.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.   <title>脏值检测</title> 
  8. </head> 
  9.  
  10. <body> 
  11.   <h3>脏值检测</h3> 
  12.   <button a-click="add" style="width: 40%; height: 50px;">增加</button> 
  13.   <button a-click="reset" style="width: 40%; height: 50px;">重置</button> 
  14.   <div> 
  15.     <span>第一个绑定数据:</span> 
  16.     <span id="aa" style="color:#CC6600" a-bind="counter"></span> 
  17.   </div> 
  18.   <div> 
  19.     <span>第二个绑定数据:</span> 
  20.     <span style="color:#CCCC33" a-bind="counter"></span> 
  21.   </div> 
  22.   <div> 
  23.     <span>第三个绑定数据:</span> 
  24.     <span style="color:#336699" a-bind="counter"></span> 
  25.   </div> 
  26.   <script type="text/javascript"
  27.     window.onload = function () { 
  28.       // 首次加载需要初始化数据 
  29.       apply() 
  30.       bind() 
  31.     } 
  32.     // data 
  33.     let counter = 0 
  34.     // methods 
  35.     function add() { 
  36.       counter++ 
  37.     } 
  38.  
  39.     function reset() { 
  40.       counter = 0 
  41.     } 
  42.     // bind event 
  43.     function bind() { 
  44.       let list = document.querySelectorAll("[a-click]"
  45.       list.forEach(item => { 
  46.         item.onclick = function () { 
  47.           window[item.getAttribute("a-click")]() 
  48.           apply() 
  49.         } 
  50.       }) 
  51.     } 
  52.     // bind data 
  53.     function apply() { 
  54.       let list = document.querySelectorAll("[a-bind='counter']"
  55.       list.forEach(item => { 
  56.         if (item.innerHTML !== counter + '') { 
  57.           item.innerHTML = counter 
  58.         } 
  59.       }) 
  60.     } 
  61.   </script> 
  62. </body> 
  63.  
  64. </html> 

02 Object.defineProperty(ES5)

该方法是目前比较主流的方法,兼容性也不错,支持 ie8(注意:下面并没实现 vue2 的发布订阅者模式,有空再撸一个出来)。

  1. <!DOCTYPE html> 
  2. <html lang="zh-CN"
  3.  
  4. <head> 
  5.   <meta charset="UTF-8"
  6.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.   <title>Object.defineProperty</title> 
  8. </head> 
  9.  
  10. <body> 
  11.   <h3>Object.defineProperty(ES5语法)</h3> 
  12.   <button a-click="add" style="width: 40%; height: 50px;">增加</button> 
  13.   <button a-click="reset" style="width: 40%; height: 50px;">重置</button> 
  14.   <div> 
  15.     <span>第一个绑定数据:</span> 
  16.     <span style="color:#CC6600" a-bind="counter"></span> 
  17.   </div> 
  18.   <div> 
  19.     <span>第二个绑定数据:</span> 
  20.     <span style="color:#CCCC33" a-bind="counter"></span> 
  21.   </div> 
  22.   <div> 
  23.     <span>第三个绑定数据:</span> 
  24.     <span style="color:#336699" a-bind="counter"></span> 
  25.   </div> 
  26.   <script type="text/javascript"
  27.     window.onload = function () { 
  28.       // 首次加载需要初始化数据 
  29.       apply('counter', obj.counter) 
  30.       bind() 
  31.     } 
  32.     // data 
  33.     let obj = { 
  34.       _counter: 0 
  35.     } 
  36.     // counter 只是一个载体,真正的值存储在 _counter 
  37.     Object.defineProperty(obj, 'counter', { 
  38.       get: function () { 
  39.         //console.log('get:', counter) 
  40.         return this._counter 
  41.       }, 
  42.       set: function (val) { 
  43.         this._counter = val 
  44.         //console.log('set:', counter) 
  45.         apply('counter'this._counter) 
  46.       } 
  47.     }) 
  48.     // methods 
  49.     function add() { 
  50.       obj.counter++ 
  51.     } 
  52.  
  53.     function reset() { 
  54.       obj.counter = 0 
  55.     } 
  56.     // bind event 
  57.     function bind() { 
  58.       let list = document.querySelectorAll('[a-click]'
  59.       list.forEach(item => { 
  60.         item.onclick = function () { 
  61.           window[item.getAttribute('a-click')]() 
  62.         } 
  63.       }) 
  64.     } 
  65.     // bind data 
  66.     function apply(str, val) { 
  67.       let list = document.querySelectorAll(`[a-bind=${str}]`) 
  68.       list.forEach(item => { 
  69.         if (item.innerHTML !== val + '') { 
  70.           item.innerHTML = val 
  71.         } 
  72.       }) 
  73.     } 
  74.   </script> 
  75. </body> 
  76.  
  77. </html> 

03 Proxy(ES6)

相比上面两种方法,用 es6 Proxy 来写数据绑定,代码会直观很多,而且很易用,不过遗憾的是 Proxy 兼容性很差,IE 是全面不支持它,而且 babel 没法完全将它转为 es5 语法,虽然有 google 大佬写的 Polyfill,但那个也是有残缺的(不知道尤大在 vue3 里怎么解决它)。

  1. <!DOCTYPE html> 
  2. <html lang="zh-CN"
  3.  
  4. <head> 
  5.   <meta charset="UTF-8"
  6.   <meta name="viewport" content="width=device-width, initial-scale=1.0"
  7.   <title>proxy</title> 
  8. </head> 
  9.  
  10. <body> 
  11.   <h3>proxy(ES6语法)</h3> 
  12.   <button a-click="add" style="width: 40%; height: 50px;">增加</button> 
  13.   <button a-click="reset" style="width: 40%; height: 50px;">重置</button> 
  14.   <div> 
  15.     <span>第一个绑定数据:</span> 
  16.     <span style="color:#CC6600" a-bind="counter"></span> 
  17.   </div> 
  18.   <div> 
  19.     <span>第二个绑定数据:</span> 
  20.     <span style="color:#CCCC33" a-bind="counter"></span> 
  21.   </div> 
  22.   <div> 
  23.     <span>第三个绑定数据:</span> 
  24.     <span style="color:#336699" a-bind="counter"></span> 
  25.   </div> 
  26.   <script type="text/javascript"
  27.     window.onload = function () { 
  28.       // 首次加载需要初始化数据 
  29.       apply('counter', obj.counter) 
  30.       bind() 
  31.     } 
  32.  
  33.     // data 
  34.     let obj = new Proxy({ 
  35.       counter: 0 
  36.     }, { 
  37.       set: function (obj, prop, value) { 
  38.         obj[prop] = value 
  39.         if (prop == 'counter') { 
  40.           apply('counter', value) 
  41.         } 
  42.         return true 
  43.       } 
  44.     }) 
  45.     // methods 
  46.     function add() { 
  47.       obj.counter++ 
  48.     } 
  49.  
  50.     function reset() { 
  51.       obj.counter = 0 
  52.     } 
  53.     // bind event 
  54.     function bind() { 
  55.       let list = document.querySelectorAll('[a-click]'
  56.       list.forEach(item => { 
  57.         item.onclick = function () { 
  58.           window[item.getAttribute('a-click')]() 
  59.         } 
  60.       }) 
  61.     } 
  62.     // bind data 
  63.     function apply(str, val) { 
  64.       let list = document.querySelectorAll(`[a-bind=${str}]`) 
  65.       list.forEach(item => { 
  66.         if (item.innerHTML !== val + '') { 
  67.           item.innerHTML = val 
  68.         } 
  69.       }) 
  70.     } 
  71.   </script> 
  72. </body> 
  73.  
  74. </html> 

04 总结

除了上面三种方式外,其实原本还有一种 Object.observe 方法,该方法是在 es7 的草案中,不过经过各位大佬的讨论,还是废弃了这种方法,只有 chrome 曾经支持过(没错,是曾经,现在不支持了),这里就不鞭尸了(懒)。上面三种方式,无疑 proxy 是一个趋势,vue3 也改用它了,相信未来几年,proxy 会得到各个技术人的热捧。

 

 

责任编辑:张燕妮 来源: segmentfault.com
相关推荐

2010-09-26 16:31:13

随机查询语句

2016-01-27 10:25:31

数据分析数据架构数据价值

2023-03-06 08:40:43

RedisListJava

2022-05-27 11:33:02

前端代码设计模式

2020-10-28 10:10:03

Java单链表数据结构

2017-06-29 14:12:13

SQL ServerMysqlOracle

2016-11-10 13:00:32

网络传输协议pythonhttp

2010-07-30 11:03:54

Flex数据绑定

2015-10-20 11:12:16

数据公司部门

2010-07-30 10:30:58

Flex数据绑定

2010-04-20 12:00:01

负载均衡技术

2010-07-05 18:32:25

2010-08-31 11:14:32

2015-11-12 10:06:28

数据中心模块化

2012-08-07 10:02:06

JSP

2014-09-10 10:07:50

工程师前端工程师

2021-11-23 10:30:35

Android技术代码

2017-04-19 16:30:51

SDNNFV网络

2010-09-26 17:09:22

日内数据保护

2017-07-14 15:07:23

点赞
收藏

51CTO技术栈公众号