今天學習一下js實現vue.js中的雙向繫結
阿新 • • 發佈:2019-02-18
看完後,越來越感覺到JavaScript原生的強大了。
話不多,原始碼附上
實現效果圖:
HTML:
<div id="app"> <input type="text" zf-model="msg" /> <input type="text" zf-model="name"/> {{msg}} {{name}} <div>逍遙君</div> <div>{{name}}</div> </div> <div id="app_1"> </div>
js:
<script> //將資料和節點掛載在一起 let obj = {msg:new ViewModel(''),name:new ViewModel('')}; function ViewModel(data){ this.data = data; //this.data代表的是當前的值 (new一個對應一個) this.nodes = [];//放節點的盒子 }; ViewModel.prototype.bindNode = function(node){ //這裡要做的事就是把節點和資料繫結在一起(prototype可以讓你向物件新增屬性和方法) this.nodes.push(node);//陣列中新增input // console.log(this.nodes) }; ViewModel.prototype.setVal = function(newVal){ //這裡要做的是將input的值賦給{{}} if(newVal !== this.data){ this.data = newVal; this.update() }; }; ViewModel.prototype.getVal = function(){ //把該方法暴露,取得data的值 return this.data } ViewModel.prototype.update = function(){ //更新方法 可以把節點依次渲染成想要的結果 this.nodes.forEach(node=>{ if(node.nodeType === 1 ){ node.value = this.data; }else{ node.textContent = node.my.replace(/\{\{([^}]*)}\}/g,function(){ return obj[arguments[1]].data; }) } }) } function compile(el){ let ele =document.querySelector(el); //取元素 //我們不要直接操作節點 可能會導致頁面的迴流 let fragment = document.createDocumentFragment() //建立一個文件碎片 //取ele下的第一個元素 直到取完為止並且將內容放到文件碎片中 let child; while(child = ele.firstChild){ fragment.appendChild(child); }; function replace(fragment){ //用來遞迴判斷是否有我們想要的標籤 // NodeList 類陣列 Array.prototype.slice.call Array.from(fragment.childNodes).forEach(node=>{ //childNodes取他的兒子 forEach遍歷陣列 // console.log(node) //判斷node 節點是標籤 還是文字 if(node.nodeType === 1){ //元素節點 // node.attributes; //取到節點上的所有屬性 這還是一個類陣列 Array.from(node.attributes).forEach(attr=>{ // console.dir(attr); //console.dir 顯示一個物件的所有屬性和方法 let {name,value} = attr; //相當於 let name = attr.name; let value = attr.value if(name.includes('zf-')){ //array.includes是判斷數組裡是否包含某一元素,返回true和false obj[value].bindNode(node) //給對應的input新增上bindNode這個方法 node.addEventListener("input",function(e){ obj[value].setVal(e.target.value) }) }; }); //取得節點上的所有屬性 }; let reg = /\{\{([^}]*)\}\}/g; let text = node.textContent; //取得node文字節點中的數值用來做正則判斷 //test方法用來檢索字串中是否有匹配的文字,有返回true.否則false if(node.nodeType === 3 &®.test(text)){//文字節點 text.replace(reg,function(){ // console.log(arguments) //獲取到函式中的所有引數 node.my = text ; //自定義屬性 保留原有的值 obj[arguments[1]].bindNode(node);//給對應的text新增上bindNode這個方法 }); }; if(node.childNodes.length){ replace(node); //如果有巢狀關係 繼續查詢 }; }); }; replace(fragment); //編譯後要呼叫update方法 Object.keys(obj).forEach(key=>{ //Object.keys(),該方法返回一個數組,可以拿到物件的所有屬性 obj[key].update(); }) // 這裡操作資料 是不會導致頁面迴流 ele.appendChild(fragment); }; compile('#app') </script>