vue解析指令compile源碼層面使用解析
概述
上篇文章我們已經介紹瞭Vue的響應式原理,並實現瞭對數據的監聽,監聽的目的是為瞭及時更新視圖,所以這篇文章就來介紹下vue解析指令並初始化視圖部分。
compile
在Vue的構造函數中對根元素進行編譯
class MVue { constructor (options) { // 保存options this.$options = options this.$data = options.data // 將data進行響應式處理 observe(this.$data) // 代理 proxy(this) // 編譯 /**核心部分**/ new Compile(options.el, this) } } class Compile { constructor (el, vm) { this.$vm = vm this.$el = document.querySelector(el) if (this.$el) { this.compile(this.$el) } } compile (node) { // 找到該元素的子節點 const childNodes = node.childNodes // childNodes是類數組對象 Array.from(childNodes).forEach(n => { // 判斷類型 if (this.isElment(n)) { this.compileElement(n) // 遞歸 if (n.childNodes.length > 0) { this.compile(n) } } else if (this.isInter(n)) { // 動態插值表達式 編譯文本 this.compileText(n) } }) } isElment (node) { return node.nodeType === 1 } isInter (n) { return n.nodeType === 3 && /\{\{(.*)\}\}/.test(n.textContent) } isDir (attrName) { return attrName.startsWith('m-') } isEvent (attrName) { return attrName.startsWith('@') } // 編譯元素:遍歷它的所有屬性,看是否m-開頭指令,或者@事件 compileElement (node) { const attrs = node.attributes Array.from(attrs).forEach(attr => { // m-text="XXX" // name = m-text, value=xxx const attrName = attr.name const exp = attr.value // 是指令 if (this.isDir(attrName)) { // 執行特定指令處理函數 const dir = attrName.substring(2) this[dir] && this[dir](node, exp) } else if (this.isEvent(attrName)) { //是事件 const event = attrName.substring(1) // 通過bind改變this指向為vm實例,以便方法內部訪問vm.data中的數據 node.addEventListener(event, this.$vm.$methods[exp].bind(this.$vm)) } }) } compileText (n) { // 獲取表達式 // n.textContent = this.$vm[RegExp.$1] n.textContent = this.$vm[RegExp.$1.trim()] } text (node, exp) { node.textContent = this.$vm[exp] || exp } html (node, exp) { node.innerHTML = this.$vm[exp] } model (node, exp) { node.value = this.$vm[exp] } }
測試代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <p>{{ counter }}</p> <p m-text="測試"></p> <p m-text="counter"></p> <p m-html="desc"></p> <input m-model="name" /> </div> </body> <script src="./index.js"></script> <script> const app = new MVue({ el: '#app', data: { counter: 1, desc: '<span style = "color: red">super wanan</span>', name: 'wanan' } }) // setInterval(() => { // app.counter++ // }, 1000) </script> </html>
結果
延伸及重點講解
1. 類數組對象
node.childNodes和node.attributes都是一種類數組對象,NodeList用於保存一組有序的節點。可以通過方括號語法來訪問NodeList的值,有item方法與length屬性。它並不是Array的實例,沒有數組對象的方法。
比如我們再compile方法中打印childNodes,會得到這樣的結果:
因此需要使用Array.from()方法將類數組對象轉換為真正數組。
2. RegExp.$1
RegExp 是javascript中的一個內置對象。為正則表達式。
RegExp.$1是RegExp的一個屬性,指的是與正則表達式匹配的第一個 子匹配(以括號為標志)字符串,以此類推,RegExp.$2,RegExp.$3,…RegExp.$99總共可以有99個匹配
其實RegExp這個對象會在我們調用瞭正則表達式的方法後, 自動將最近一次的結果保存在裡面, 所以如果我們在使用正則表達式時, 有用到分組, 那麼就可以直接在調用完以後直接使用RegExp.$xx來使用捕獲到的分組內容。
所以我們在compileText時可以這樣使用:
compileText (n) { // 獲取表達式 // n.textContent = this.$vm[RegExp.$1] n.textContent = this.$vm[RegExp.$1.trim()] }
需要註意的是,上述代碼中還使用瞭trim方法去除前後空格,這是為瞭將{{ counter }}前後的空格去掉,正確匹配到this.vm[‘counter’],否則會讀取this.vm[’ counter ']導致讀取不到。
3. nodeType
DOM(文檔對象模型)可以將任何HTML和XML文檔描繪成一個由多層node(節點)構成的結構。
DOM1級定義瞭一個Node接口,該接口將由DOM中的所有節點類型實現。這個Node接口在JavaScript中作為Node類型實現的。除瞭IE之外,在其他所有瀏覽器中都可以訪問到這個類型。JavaScript中的所有節點類型都繼承自Node類型,因此所有節點類型都共享相同的基本屬性和方法。每個節點都有一個nodeType屬性,用於表明節點的類型。
nodeType的取值如下:
類型 | nodeType常數值 | 描述 |
---|---|---|
Node.ELEMENT_NODE | 1 | 元素節點 |
Node.ATTRIBUTE_NODE | 2 | 屬性節點 |
Node.TEXT_NODE | 3 | 文本節點 |
Node.CDATA_SECTION_NODE | 4 | 字符數據節點(文本不會被解析器解析) |
Node.ENTITY_REFERENCE_NODE | 5 | 實體引用節點 |
Node.ENTITY_NODE | 6 | 實體節點 |
Node.PROCESSING_INSTRUCTION_NODE | 7 | 處理指令節點 |
Node.COMMENT_NODE | 8 | 註釋節點 |
Node.DOCUMENT_NODE | 9 | 文檔節點(DOM樹的根節點) |
Node.DOCUMENT_TYPE_NODE | 10 | 向為文檔定義的實體提供接口 |
Node.DOCUMENT_FRAGMENT_NODE | 11 | 表示鄰接節點和它們的子樹 |
Node.NOTATION_NODE | 12 | 代表一個符號在DTD中的聲明 |
到此這篇關於vue解析指令compile源碼層面使用解析的文章就介紹到這瞭,更多相關vue compile內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- vue MVVM雙向綁定實例詳解(數據劫持+發佈者-訂閱者模式)
- 詳細聊聊Vue.js中的MVVM
- 手動實現vue2.0的雙向數據綁定原理詳解
- vue2之響應式雙向綁定,在對象或數組新增屬性頁面無響應的情況
- Manipulation-TypeScript DOM操作示例解析