前端的三種數(shù)據(jù)綁定技術(shù)
本喵寫了以下三種不同方式的數(shù)據(jù)綁定(只實(shí)現(xiàn)了單向綁定):
- 第一種,使用了“臟值”檢測(cè),該方法是 angular 的數(shù)據(jù)綁定原理。
- 第二種,使用了 es5 的 Object.defineProperty(),vue2 的數(shù)據(jù)綁定就是基于該方法。
- 第三種,使用了 es6 的 Proxy ,vue3 的數(shù)據(jù)綁定開始全盤換為這種方式。
廢話不多說(shuō),直接擼起代碼~
01 臟值檢測(cè)
如果綁定的數(shù)據(jù)過(guò)多,臟值檢測(cè)可能會(huì)造成性能問題,因?yàn)槊看胃淖冎?,都需要進(jìn)行輪詢改變對(duì)應(yīng)的值。
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>臟值檢測(cè)</title>
- </head>
- <body>
- <h3>臟值檢測(cè)</h3>
- <button a-click="add" style="width: 40%; height: 50px;">增加</button>
- <button a-click="reset" style="width: 40%; height: 50px;">重置</button>
- <div>
- <span>第一個(gè)綁定數(shù)據(jù):</span>
- <span id="aa" style="color:#CC6600" a-bind="counter"></span>
- </div>
- <div>
- <span>第二個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#CCCC33" a-bind="counter"></span>
- </div>
- <div>
- <span>第三個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#336699" a-bind="counter"></span>
- </div>
- <script type="text/javascript">
- window.onload = function () {
- // 首次加載需要初始化數(shù)據(jù)
- 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>
02 Object.defineProperty(ES5)
該方法是目前比較主流的方法,兼容性也不錯(cuò),支持 ie8(注意:下面并沒實(shí)現(xiàn) vue2 的發(fā)布訂閱者模式,有空再擼一個(gè)出來(lái))。
- <!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語(yǔ)法)</h3>
- <button a-click="add" style="width: 40%; height: 50px;">增加</button>
- <button a-click="reset" style="width: 40%; height: 50px;">重置</button>
- <div>
- <span>第一個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#CC6600" a-bind="counter"></span>
- </div>
- <div>
- <span>第二個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#CCCC33" a-bind="counter"></span>
- </div>
- <div>
- <span>第三個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#336699" a-bind="counter"></span>
- </div>
- <script type="text/javascript">
- window.onload = function () {
- // 首次加載需要初始化數(shù)據(jù)
- apply('counter', obj.counter)
- bind()
- }
- // data
- let obj = {
- _counter: 0
- }
- // counter 只是一個(gè)載體,真正的值存儲(chǔ)在 _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>
03 Proxy(ES6)
相比上面兩種方法,用 es6 Proxy 來(lái)寫數(shù)據(jù)綁定,代碼會(huì)直觀很多,而且很易用,不過(guò)遺憾的是 Proxy 兼容性很差,IE 是全面不支持它,而且 babel 沒法完全將它轉(zhuǎn)為 es5 語(yǔ)法,雖然有 google 大佬寫的 Polyfill,但那個(gè)也是有殘缺的(不知道尤大在 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語(yǔ)法)</h3>
- <button a-click="add" style="width: 40%; height: 50px;">增加</button>
- <button a-click="reset" style="width: 40%; height: 50px;">重置</button>
- <div>
- <span>第一個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#CC6600" a-bind="counter"></span>
- </div>
- <div>
- <span>第二個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#CCCC33" a-bind="counter"></span>
- </div>
- <div>
- <span>第三個(gè)綁定數(shù)據(jù):</span>
- <span style="color:#336699" a-bind="counter"></span>
- </div>
- <script type="text/javascript">
- window.onload = function () {
- // 首次加載需要初始化數(shù)據(jù)
- 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>
04 總結(jié)
除了上面三種方式外,其實(shí)原本還有一種 Object.observe 方法,該方法是在 es7 的草案中,不過(guò)經(jīng)過(guò)各位大佬的討論,還是廢棄了這種方法,只有 chrome 曾經(jīng)支持過(guò)(沒錯(cuò),是曾經(jīng),現(xiàn)在不支持了),這里就不鞭尸了(懶)。上面三種方式,無(wú)疑 proxy 是一個(gè)趨勢(shì),vue3 也改用它了,相信未來(lái)幾年,proxy 會(huì)得到各個(gè)技術(shù)人的熱捧。