01:vue的模板替換簡單實現
阿新 • • 發佈:2020-12-15
1.Vue與模板
vue是如何使用的:
- 編寫頁面模板
- 建立Vue例項
- 在Vue建構函式中提供:data、methods、computed...
- 將Vue掛載到頁面中(mount)
下面是一個程式碼例項:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <!-- 第一步:寫模板 --> <div id="root"> <h1>姓名:{{name}}——年齡:{{age}}</h1> </div> <script> // 第二步:建立Vue例項 let app=new Vue({ el:'#root', data:{ name:'xxx', age:14 } }) // 第三步:掛載。這種用法的掛載在vue.js中幫我們實現了 </script> </body> </html>
2.資料驅動模型
Vue的執行流程:
- 獲得模板:模板中有預留的坑(如{{}}、:等)
- 利用Vue建構函式中所提供的資料來填補預留的坑,就可以在頁面上顯示標籤 了
- 將標籤替換頁面中原來有坑的標籤
Vue利用我們提供的資料和頁面中的模板,生成了一個新的HTML標籤,替換到了頁面中放置模板的位置。
比如:在上面那個例子中列印那個root標籤:
<script> console.log(root); let app=new Vue({ el:'#root', data:{ name:'xxx', age:14 } }) console.log(root); </script>
控制檯結果:
- 此時如果將滑鼠懸浮在第二次列印的root上面,頁面上會高亮表示選中的是當前的標籤
- 但是將滑鼠懸浮在第一次列印的結果上沒有高亮,表明建立Vue例項之後的root的標籤已經不是頁面剛載入時的那個標籤了
3.一個簡單的模板填充資料的例項:
這個部分的作用是把頁面模板上帶有花括號的內容全部替換成具體的資料:<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- 第一步:寫模板 --> <div id="root"> <div> <div> <h1>姓名:{{name}}——年齡:{{age}}</h1> </div> </div> <h2>姓名:{{name}}</h2> <h2>年齡:{{age}}</h2> </div> <script> let r = /\{\{(.+?)\}\}/g /** * 步驟拆解: * 1.拿到模板 * 2.拿到資料(data) * 3.將資料與模板結合,得到的是HTML元素(DOM元素) * 4.放到頁面中 * */ // 1 let tempNode = document.getElementById('root') // 2 let data = { name: 'xlx', age: 26 } // 3.將資料放到模板中:一般都是使用遞迴 // 在這個案例中,template是一個DOM元素 // 在真正的VUe原始碼中是將DOM元素轉換成字串模板 -> 轉換成抽象語法樹 -> 轉換成虛擬DOM(VNode) -> 轉換成真正的DOM function compiler(template, data) { // 這個函式,把花括號裡面的資料用data裡面所對應的屬性進行替換 // 把子元素中所有帶雙花括號的元素取出來 let childNodes = template.childNodes // 取出子元素 for (let i = 0; i < childNodes.length; i++) { let type = childNodes[i].nodeType // nodeType表示這個節點的型別:1表示元素節點;3表示文字節點(不同瀏覽器不同,這裡只需要記住這兩個) if (type === 3) { // 文字節點,可以判斷裡面是否有 {{}} 插值 let txt = childNodes[i].nodeValue // nodeValue該屬性只有文字節點才有意義 // 有沒有{{}}呢 txt = txt.replace(r, function (_, g) { // replace方法,使用正則匹配了一次,這個函式就會被呼叫一次。return的值就是替換的匹配到的值 // 引數一:表示匹配到的內容 // 引數二:表示正則中的第n組:g.trim()表示寫在雙花括號裡面的東西 let key = g.trim(); let value = data[key] // 將{{ xxx }}用這個值(data中對應的值)替換 return value }) // 注意:txt現在和DOM元素是沒有關係的,需要將修改後的值放回去 childNodes[i].nodeValue = txt } else if (type === 1) { // 元素節點,考慮有沒有子元素,是否需要將其子元素進行判斷是否要插值 compiler(childNodes[i], data) // 遞迴處理 } } } // 我們此時是沒有生成新的template,所以這裡看到的是直接在頁面中就更新的資料,因為DOM是引用型別 // 但是我們這麼處理的話,原來帶{{}}的模板就沒有了。無法再更新了
// 利用模板生成一個需要被渲染的HTML標籤(準·真正在頁面中顯示的標籤) let generateNode = tempNode.cloneNode(true) // 注意這裡是DOM元素,可以直接這麼用 console.log(1, tempNode); compiler(generateNode, data) console.log(2, generateNode); // 4.將渲染好的HTML加到頁面當中 root.parentNode.replaceChild(generateNode, root) // 到此為止:上面的思路有比較大的問題 // 1.Vue使用的是虛擬DOM,而這裡用的是真實的DOM // 2.只考慮了單屬性{{ name }},而Vue中大量的使用了層級({{ child.name }}) // 3.程式碼沒有整合 </script> </body> </html>
頁面結果如下:
說明:這個實現的大致理論: