前端的三种数据绑定技术

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

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

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

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

[[360473]]

01 脏值检测

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

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

02 Object.defineProperty(ES5)

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

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

03 Proxy(ES6)

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

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

04 总结

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

 

 

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

2025-02-17 11:08:08

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单链表数据结构

2010-07-30 11:03:54

Flex数据绑定

2016-11-10 13:00:32

网络传输协议pythonhttp

2017-06-29 14:12:13

SQL ServerMysqlOracle

2010-04-20 12:00:01

负载均衡技术

2015-10-20 11:12:16

数据公司部门

2010-07-30 10:30:58

Flex数据绑定

2010-07-05 18:32:25

2010-08-31 11:14:32

2012-08-07 10:02:06

JSP

2021-11-23 10:30:35

Android技术代码

2015-11-12 10:06:28

数据中心模块化

2014-09-10 10:07:50

工程师前端工程师

2011-01-18 15:35:59

jQueryJavaScriptweb

2017-04-19 16:30:51

SDNNFV网络
点赞
收藏

51CTO技术栈公众号