動態

詳情 返回 返回

js實現數據單向綁定 - 動態 詳情

如果你在學習一種前端框架,如vue、angular等,那麼你一定不會對數據的單向綁定陌生。

何為數據的單向綁定?

傳統開發模式下,如使用jQuery開發,我們想將一個變量顯示到html中,首先要定義一個變量name,然後通過jq代碼操作dom將變量放到HTML中,如果name發生修改,還要再次通過jq代碼操作dom將新的變量值放到HTML中。這就是傳統的MVC框架,其中的Model和View是我們通過代碼聯繫在一起的。
在MVVM框架中,我們不再過多的關注數據與視圖間的操作,而是使用一種新的機制,數據的單/雙向綁定。
我在剛接觸angular的時候感覺這個綁定的機制簡直不要再神奇,但是當慢慢深入學習後發現其實原理非常簡單,通過簡短的js代碼就可以實現一個簡單的數據單向綁定。

Proxy對象

Proxy可以當做在目標對象之前架設一層攔截,或者説是代理。任何對該對象的訪問都要先經過這個代理。那麼也就是説我們可以通過Proxy對象攔截到外界對一個對象的訪問。
ES6中將Proxy標準化了,提供了Proxy構造函數,用來生成Proxy實例。下面就是官方的定義:

let p = new Proxy(target, handler);
  • target:用Proxy包裝的目標對象(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理)
  • handler:一個對象,其屬性是當執行一個操作時定義代理的行為的函數。

再看一個栗子:

let p = new Proxy({}, {
    get: function(target, name){
        return name in target ? target[name] : 37;
    }
});
p.a = 1;
p.b = undefined;
console.log(p.a, p.b);    // 1, undefined
console.log('c' in p, p.c);    // false, 37

栗子中通過Proxy對象攔截了p對象的訪問,當對象中不存在屬性名時返回37,如果有就返回屬性值。
到這已經瞭解了Proxy對象的工作方式,我們就要用Proxy來做點事了。

單向綁定

首先
如果你接觸過一些mvvm框架,那麼一定會對下面的代碼非常熟悉

<div id="app">
  姓名:{{name}}
  <br> 年齡:{{age}}
</div>

“{{}}”叫做插值表達式,vue、angular中都是使用這種方式進行數據的單向綁定。在這我們也使用這種格式綁定數據。
然後

let el = document.getElementById('app');
let template = el.innerHTML;

上面兩步,首先獲取到根元素,然後將根元素下的html保存下來。這裏為什麼要保存原始的html呢?
因為接下來的數據變化會重新編譯標籤,既然要重新編譯,那麼就要保留最初的狀態,否則編譯一次後,第二次就無法正常編譯了。
再然後

let _data = {
  name: '_BuzzLy',
  age: 25
};

定義一個_data對象,這個對象是給我們接下來創建的Proxy對象用的,並不是暴露出去供訪問的。
接下來

let data = new Proxy(_data, {
  // 試圖設置數據時調用
  // 參數:_data,屬性名,值
  set(obj, name, value) {
    obj[name] = value;

    // 數據變了
    console.log(`數據變了,設置 ${name}=>${value}`);
    // 數據改變後重新渲染
    render();
  },
  // 試圖獲取數據的時調用,默認要什麼就返回什麼
  // get() {}
});

這步我們創建一個data,這個data就是對外的,是一個Proxy對象。
Proxy是原生的對象,可以將真正的數據對象隱藏,我們修改的是代理對象。
相當於我們想修改_data一定要經過一步代理,告訴代理我們要修改的對象及修改的值,然後代理去幫我們操作。
在修改完成後調用render函數去重新渲染視圖。
最後
上面已經完成了最重要的部分,當一個數據發生改變後調用了render函數,這個函數就是將改變後的數據重新渲染到視圖中去。

function render() {
    el.innerHTML = template.replace(/\{\{w+\}\}/g, str => {
        console.log(str); // 匹配出來的 {{name}}  {{age}} 
        // 截取字符串,得到屬性key值
        str = str.substring(2, str.length - 2);
        // 從真實數據中拿到對應屬性的值返回,替換{{key}}
        return _data[str];
    })
}

這裏的實現很簡單,就是匹配到html中{{key}},然後拿到key,再去_data中找到值即可。
注意這裏的template,這就是我們在上面為什麼要保存一份原始的html原因。
現在你可以去嘗試手動修改data的值看看效果了。

data.name = 'halo wode';
data.age = 18;

到這一個最簡單的數據單向綁定就實現了。


完整代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <div id="app">
    姓名:{{name}}
    <br> 年齡:{{age}}
  </div>
</body>
<script>
  let el = document.getElementById('app');
  let template = el.innerHTML;
  let _data = {
    name: '_BuzzLy',
    age: 25
  };

  let data = new Proxy(_data, {
    set(obj, name, value) {
      obj[name] = value;
      render();
    }
  });
  
  render();

  function render() {
    el.innerHTML = template.replace(/\{\{\w+\}\}/g, str => {
      str = str.substring(2, str.length - 2);
      return _data[str];
    });
  }
</script>
</html>
user avatar grewer 頭像 cyzf 頭像 zaotalk 頭像 guochenglong 頭像 nihaojob 頭像 jingdongkeji 頭像 qingzhan 頭像 dirackeeko 頭像 razyliang 頭像 longlong688 頭像 anchen_5c17815319fb5 頭像 huichangkudelingdai 頭像
點贊 205 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.