js實現數據單向綁定

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

何爲數據的單向綁定?

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

Proxy對象

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

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框架,那麼必定會對下面的代碼很是熟悉app

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

「{{}}」叫作插值表達式,vue、angular中都是使用這種方式進行數據的單向綁定。在這咱們也使用這種格式綁定數據。
而後框架

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

上面兩步,首先獲取到根元素,而後將根元素下的html保存下來。這裏爲何要保存原始的html呢?
由於接下來的數據變化會從新編譯標籤,既然要從新編譯,那麼就要保留最初的狀態,不然編譯一次後,第二次就沒法正常編譯了。
再而後dom

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

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

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>
相關文章
相關標籤/搜索