1. 程式人生 > 實用技巧 >實現前端框架資料繫結(資料驅動DOM)

實現前端框架資料繫結(資料驅動DOM)

一: 完整程式碼

function  $(element){    //傳入響應式區域的dom根節點,返回響應式資料

    let  $$  =  {};    //響應式資料

    function  getElements(el){    //傳入一個元素節點,返回一個儲存著該元素本身及所有子元素的陣列
        var  elementList  =  [],    //儲存所有的元素節點(最終結果)
            tempList  =  [],    //暫時性的儲存有子節點的元素節點
            parentElement  =  [];    //永遠儲存上一級的元素
parentElement.push(el); elementList.push(parentElement[0]); //把根元素推入list while(parentElement.length != 0){ for(let i = 0, len = parentElement.length; i < len; ++i){ let childElement = parentElement[i].childNodes;
for(let j = 0, lens = childElement.length; j < lens; ++j){ if(childElement[j].nodeType == 1){ elementList.push(childElement[j]); if(childElement[j].hasChildNodes()){ tempList.push(childElement[j]); } } } } parentElement
= tempList; tempList = []; } return elementList; } function attrHanding(elements){ //傳入一個元素列表,返回bindData函式需要格式的陣列 let list = []; for(let i = 0, len = elements.length; i < len; ++i){ let temp = elements[i].attributes; for(let j = 0, lens = temp.length; j < lens; ++j){ let temps = temp[j], testinga = temps.nodeName.indexOf('a:'), testingb = temps.nodeName.indexOf('s:'); if(testinga != -1 || testingb != -1){ let tem = temps.nodeName.split('&'), end = '', tems = (tem[0].split(':'))[1]; tems = tems.split(''); for(let x = 0, l = tems.length; x < l; ++x){ if(tems[x] != '-'){ end = end + tems[x]; }else{ ++x; end = end + tems[x].toUpperCase(); } } list.push(tem[1], elements[i]); if(testinga != -1){ list.push(end, '', temp[j].nodeValue); }else{ list.push('style', end, temp[j].nodeValue); } } } } return list; } function bindData(s){ //需要給bind傳入陣列,5個一組:(dom替代的名字,要操作的dom元素,要操作的屬性名1,要操作的屬性名2,dom替代的名字初始值) for(let i = 0, l = s.length; i < l; i += 5){ //把$$裡的資料和dom繫結到一起,$$發生變動時,自動修改dom Object.defineProperty($$, s[i], { get: function(){ return this['_' + s[i]]; }, set: function(v){ this['_' + s[i]] = v; if(s[i + 3] == ""){ s[i + 1][s[i + 2]] = v; }else{ s[i + 1][s[i + 2]][s[i + 3]] = v; } } }); $$[s[i]] = s[i + 4]; //初始值 } } bindData(attrHanding(getElements(element))); return $$; }

二:語法說明

1:對於一般的html元素屬性,可以這樣繫結 a:屬性名&繫結的變數名='初始值'

比如: a:title&tit="Hello DataBinding"(將title屬性和變數tit繫結在一起)

2:對於行內樣式,需要這樣繫結 s:屬性名&繫結的變數名='初始值'

比如: s:color&color="red"(將行內樣式color屬性和color變數繫結在一起)

3:引入完整程式碼後,把你需要控制的區域作為一個元素傳入$()函式,它會返回一個物件,你需要接收它,裡面包含了所有響應式的元素屬性

比如:我定義了data變數來接受,那麼我就可以用data.變數名=" "這樣的方式來操縱它

三:需要注意的點

因為html不區分大小寫,所以變數名需要小寫,對於包含大寫字母的屬性,比如innerHTML,需要這樣寫:inner-h-t-m-l

四:案例演示

新建一個資料夾,進入,新建一個databinding.js檔案,把完整程式碼貼上進去,儲存,再新建一個html檔案,貼上以下程式碼,儲存,然後雙擊開啟,在瀏覽器裡應該可以看到效果

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>資料繫結</title>
</head>
<body>
<div id="root">
<p a:title&tit="Hello" s:font-weight&weight="300">請把滑鼠移動到我身上</p>
<button onclick="coarsen()">點選我讓字型變粗</button>
</div>
<script src="./databinding.js"></script>
<script>
var data = $(document.getElementById('root'));
data.tit = "如果看到我,說明你繫結成功";
function coarsen(){
data.weight = "700";
}
</script>
</body>
</html>

五:實現思路及程式碼組織

遍歷需要控制區域的所有元素(封裝函式getElements),

遍歷每個元素的所有屬性,找到符合語法的屬性進行解析(封裝函式attrHanding),

利用Object.defineProperty()函式重定義get和set(封裝函式bindData),

然後定義函式$,在$函式內定義一個空物件{}用於存放響應式屬性變數,

再把前面的三個函式放到$函式內,依次巢狀呼叫bindData attrHanding getElements,返回定義的空物件