vue解析指令compile源碼層面使用解析
概述
上篇文章我們已經介紹了Vue的響應式原理,并實現了對數據的監(jiān)聽,監(jiān)聽的目的是為了及時更新視圖,所以這篇文章就來介紹下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) {
// 找到該元素的子節(jié)點
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)) {
// 動態(tài)插值表達式 編譯文本
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í)行特定指令處理函數
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用于保存一組有序的節(jié)點。可以通過方括號語法來訪問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(節(jié)點)構成的結構。
DOM1級定義了一個Node接口,該接口將由DOM中的所有節(jié)點類型實現。這個Node接口在JavaScript中作為Node類型實現的。除了IE之外,在其他所有瀏覽器中都可以訪問到這個類型。JavaScript中的所有節(jié)點類型都繼承自Node類型,因此所有節(jié)點類型都共享相同的基本屬性和方法。每個節(jié)點都有一個nodeType屬性,用于表明節(jié)點的類型。
nodeType的取值如下:
| 類型 | nodeType常數值 | 描述 |
|---|---|---|
| Node.ELEMENT_NODE | 1 | 元素節(jié)點 |
| Node.ATTRIBUTE_NODE | 2 | 屬性節(jié)點 |
| Node.TEXT_NODE | 3 | 文本節(jié)點 |
| Node.CDATA_SECTION_NODE | 4 | 字符數據節(jié)點(文本不會被解析器解析) |
| Node.ENTITY_REFERENCE_NODE | 5 | 實體引用節(jié)點 |
| Node.ENTITY_NODE | 6 | 實體節(jié)點 |
| Node.PROCESSING_INSTRUCTION_NODE | 7 | 處理指令節(jié)點 |
| Node.COMMENT_NODE | 8 | 注釋節(jié)點 |
| Node.DOCUMENT_NODE | 9 | 文檔節(jié)點(DOM樹的根節(jié)點) |
| Node.DOCUMENT_TYPE_NODE | 10 | 向為文檔定義的實體提供接口 |
| Node.DOCUMENT_FRAGMENT_NODE | 11 | 表示鄰接節(jié)點和它們的子樹 |
| Node.NOTATION_NODE | 12 | 代表一個符號在DTD中的聲明 |
到此這篇關于vue解析指令compile源碼層面使用解析的文章就介紹到這了,更多相關vue compile內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue.js移動端app之上拉加載以及下拉刷新實戰(zhàn)
這篇文章主要介紹了vue.js移動端app之上拉加載以及下拉刷新實戰(zhàn),非常具有實用價值,需要的朋友可以參考下2017-09-09
vue2.0/3.0中provide和inject的用法示例
provide和inject是成對出現的,主要用于父組件向子孫組件傳遞數據,這篇文章主要給大家介紹了關于vue2.0/3.0中provide和inject用法的相關資料,需要的朋友可以參考下2021-09-09
Vue 微信端掃描二維碼蘋果端卻只能保存圖片問題(解決方法)
這幾天在做項目時遇到微信掃描二維碼的然后進入公眾號網頁巴拉巴拉的,然后就很順利的遇到了在安卓端掃碼的時候,順利的一塌糊涂,然后到了蘋果端的時候,就只能出現一個保存圖片,然后就寫一下記錄一下這問題的解決方法2020-01-01
webpack+vue-cli項目中引入外部非模塊格式js的方法
今天小編就為大家分享一篇webpack+vue-cli項目中引入外部非模塊格式js的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09

