vue解析指令compile源碼層面使用解析
概述
上篇文章我們已經(jīng)介紹了Vue的響應(yīng)式原理,并實(shí)現(xiàn)了對(duì)數(shù)據(jù)的監(jiān)聽,監(jiān)聽的目的是為了及時(shí)更新視圖,所以這篇文章就來介紹下vue解析指令并初始化視圖部分。
compile
在Vue的構(gòu)造函數(shù)中對(duì)根元素進(jìn)行編譯
class MVue { constructor (options) { // 保存options this.$options = options this.$data = options.data // 將data進(jìn)行響應(yīng)式處理 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) { // 找到該元素的子節(jié)點(diǎn) const childNodes = node.childNodes // childNodes是類數(shù)組對(duì)象 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)) { // 動(dòng)態(tài)插值表達(dá)式 編譯文本 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)) { // 執(zhí)行特定指令處理函數(shù) const dir = attrName.substring(2) this[dir] && this[dir](node, exp) } else if (this.isEvent(attrName)) { //是事件 const event = attrName.substring(1) // 通過bind改變this指向?yàn)関m實(shí)例,以便方法內(nèi)部訪問vm.data中的數(shù)據(jù) node.addEventListener(event, this.$vm.$methods[exp].bind(this.$vm)) } }) } compileText (n) { // 獲取表達(dá)式 // 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] } }
測(cè)試代碼
<!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="測(cè)試"></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>
結(jié)果
延伸及重點(diǎn)講解
1. 類數(shù)組對(duì)象
node.childNodes和node.attributes都是一種類數(shù)組對(duì)象,NodeList用于保存一組有序的節(jié)點(diǎn)??梢酝ㄟ^方括號(hào)語法來訪問NodeList的值,有item方法與length屬性。它并不是Array的實(shí)例,沒有數(shù)組對(duì)象的方法。
比如我們?cè)賑ompile方法中打印childNodes,會(huì)得到這樣的結(jié)果:
因此需要使用Array.from()方法將類數(shù)組對(duì)象轉(zhuǎn)換為真正數(shù)組。
2. RegExp.$1
RegExp 是javascript中的一個(gè)內(nèi)置對(duì)象。為正則表達(dá)式。
RegExp.$1是RegExp的一個(gè)屬性,指的是與正則表達(dá)式匹配的第一個(gè) 子匹配(以括號(hào)為標(biāo)志)字符串,以此類推,RegExp.$2,RegExp.$3,…RegExp.$99總共可以有99個(gè)匹配
其實(shí)RegExp這個(gè)對(duì)象會(huì)在我們調(diào)用了正則表達(dá)式的方法后, 自動(dòng)將最近一次的結(jié)果保存在里面, 所以如果我們?cè)谑褂谜齽t表達(dá)式時(shí), 有用到分組, 那么就可以直接在調(diào)用完以后直接使用RegExp.$xx來使用捕獲到的分組內(nèi)容。
所以我們?cè)赾ompileText時(shí)可以這樣使用:
compileText (n) { // 獲取表達(dá)式 // n.textContent = this.$vm[RegExp.$1] n.textContent = this.$vm[RegExp.$1.trim()] }
需要注意的是,上述代碼中還使用了trim方法去除前后空格,這是為了將{{ counter }}前后的空格去掉,正確匹配到this.vm[‘counter’],否則會(huì)讀取this.vm[’ counter ']導(dǎo)致讀取不到。
3. nodeType
DOM(文檔對(duì)象模型)可以將任何HTML和XML文檔描繪成一個(gè)由多層node(節(jié)點(diǎn))構(gòu)成的結(jié)構(gòu)。
DOM1級(jí)定義了一個(gè)Node接口,該接口將由DOM中的所有節(jié)點(diǎn)類型實(shí)現(xiàn)。這個(gè)Node接口在JavaScript中作為Node類型實(shí)現(xiàn)的。除了IE之外,在其他所有瀏覽器中都可以訪問到這個(gè)類型。JavaScript中的所有節(jié)點(diǎn)類型都繼承自Node類型,因此所有節(jié)點(diǎn)類型都共享相同的基本屬性和方法。每個(gè)節(jié)點(diǎn)都有一個(gè)nodeType屬性,用于表明節(jié)點(diǎn)的類型。
nodeType的取值如下:
類型 | nodeType常數(shù)值 | 描述 |
---|---|---|
Node.ELEMENT_NODE | 1 | 元素節(jié)點(diǎn) |
Node.ATTRIBUTE_NODE | 2 | 屬性節(jié)點(diǎn) |
Node.TEXT_NODE | 3 | 文本節(jié)點(diǎn) |
Node.CDATA_SECTION_NODE | 4 | 字符數(shù)據(jù)節(jié)點(diǎn)(文本不會(huì)被解析器解析) |
Node.ENTITY_REFERENCE_NODE | 5 | 實(shí)體引用節(jié)點(diǎn) |
Node.ENTITY_NODE | 6 | 實(shí)體節(jié)點(diǎn) |
Node.PROCESSING_INSTRUCTION_NODE | 7 | 處理指令節(jié)點(diǎn) |
Node.COMMENT_NODE | 8 | 注釋節(jié)點(diǎn) |
Node.DOCUMENT_NODE | 9 | 文檔節(jié)點(diǎn)(DOM樹的根節(jié)點(diǎn)) |
Node.DOCUMENT_TYPE_NODE | 10 | 向?yàn)槲臋n定義的實(shí)體提供接口 |
Node.DOCUMENT_FRAGMENT_NODE | 11 | 表示鄰接節(jié)點(diǎn)和它們的子樹 |
Node.NOTATION_NODE | 12 | 代表一個(gè)符號(hào)在DTD中的聲明 |
到此這篇關(guān)于vue解析指令compile源碼層面使用解析的文章就介紹到這了,更多相關(guān)vue compile內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何使用Vue3+Vite+TS快速搭建一套實(shí)用的腳手架
Vite是一個(gè)面向現(xiàn)代瀏覽器的一個(gè)更輕、更快的?Web?應(yīng)用開發(fā)工具,下面這篇文章主要給大家介紹了關(guān)于如何使用Vue3+Vite+TS快速搭建一套實(shí)用腳手架的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10vue.js移動(dòng)端app之上拉加載以及下拉刷新實(shí)戰(zhàn)
這篇文章主要介紹了vue.js移動(dòng)端app之上拉加載以及下拉刷新實(shí)戰(zhàn),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-09-09Vue.js點(diǎn)擊切換按鈕改變內(nèi)容的實(shí)例講解
今天小編就為大家分享一篇Vue.js點(diǎn)擊切換按鈕改變內(nèi)容的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08vue2.0/3.0中provide和inject的用法示例
provide和inject是成對(duì)出現(xiàn)的,主要用于父組件向子孫組件傳遞數(shù)據(jù),這篇文章主要給大家介紹了關(guān)于vue2.0/3.0中provide和inject用法的相關(guān)資料,需要的朋友可以參考下2021-09-09Vue 微信端掃描二維碼蘋果端卻只能保存圖片問題(解決方法)
這幾天在做項(xiàng)目時(shí)遇到微信掃描二維碼的然后進(jìn)入公眾號(hào)網(wǎng)頁巴拉巴拉的,然后就很順利的遇到了在安卓端掃碼的時(shí)候,順利的一塌糊涂,然后到了蘋果端的時(shí)候,就只能出現(xiàn)一個(gè)保存圖片,然后就寫一下記錄一下這問題的解決方法2020-01-01webpack+vue-cli項(xiàng)目中引入外部非模塊格式j(luò)s的方法
今天小編就為大家分享一篇webpack+vue-cli項(xiàng)目中引入外部非模塊格式j(luò)s的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09