若是你在學習一種前端框架,如vue、angular等,那麼你必定不會對數據的單向綁定陌生。html
傳統開發模式下,如使用jQuery開發,咱們想將一個變量顯示到html中,首先要定義一個變量name
,而後經過jq代碼操做dom將變量放到HTML中,若是name
發生修改,還要再次經過jq代碼操做dom將新的變量值放到HTML中。這就是傳統的MVC框架,其中的Model和View是咱們經過代碼聯繫在一塊兒的。
在MVVM框架中,咱們再也不過多的關注數據與視圖間的操做,而是使用一種新的機制,數據的單/雙向綁定。
我在剛接觸angular的時候感受這個綁定的機制簡直不要再神奇,可是當慢慢深刻學習後發現其實原理很是簡單,經過簡短的js代碼就能夠實現一個簡單的數據單向綁定。前端
Proxy能夠當作在目標對象以前架設一層攔截,或者說是代理。任何對該對象的訪問都要先通過這個代理。那麼也就是說咱們能夠經過Proxy對象攔截到外界對一個對象的訪問。
ES6中將Proxy標準化了,提供了Proxy構造函數,用來生成Proxy實例。下面就是官方的定義:vue
let p = new Proxy(target, 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>