經過上一篇的分析,完成了查詢指令和模板的功能,接下來就是編譯指令的資料了。
所以本章節主要處理的方法則是 buildElement 方法,我們先分析一下我們所拿到的資料在進行編碼,這樣會更加清晰一些。
我將 name, value 列印出來,分別對應的值是 name: v-model, value: name,在今後我們的命令中可不止只有 v-model,還有 v-text、v-html、v-on 等等,所以我們需要對這些指令進行分類,然後再進行編譯。
所以我這裡特意定義了一個工具類叫 CompilerUtil,用來處理指令的分類,程式碼如下:
let CompilerUtil = {
/**
* 處理 v-model 指令
* @param node 當前元素
* @param value 指令的值
* @param vm Nue 的例項物件
*/
model: function (node, value, vm) {
},
html: function (node, value, vm) {
},
text: function (node, value, vm) {
}
}
然後我們在 buildElement 方法中呼叫這個方法,程式碼如下:
// 解構 name
let [, directive] = name.split('-');
// v-model -> [v, model]
// 2.根據指令名稱, 呼叫不同的處理函式
CompilerUtil[directive](node, value, this.vm);
這樣我們就可以根據指令的名稱,呼叫不同的處理函式了。
接下來我們就來處理 v-model 指令,程式碼如下:
/**
* 處理 model 指令
* @param node 當前元素
* @param value 指令的值
* @param vm Nue 的例項物件
*/
model: function (node, value, vm) {
node.value = vm.$data[value];
},
這樣我們就可以將資料渲染到頁面上了,開啟瀏覽器,可以看到效果如下:
v-model 指令已經可以正常使用了,但是還有問題,就是我們的資料結構目前是比較簡單的,那麼如果我們的資料是一個物件呢,例如:
time: {
h: 10,
m: 10,
s: 10
}
在用 input 繫結 v-model 進行渲染發現,只有第一個 input 能夠正常渲染,其他的 input 都是 undefined,這是為什麼呢?
<input type="text" v-model="time.h">
<input type="text" v-model="time.m">
<input type="text" v-model="time.s">
那麼這裡就要去看一下我們 model 方法的實現了,如果是 time.h,value 等於的值為 time.h, 然後我們在執行 vm.$data[value]
就變為了 vm.$data[time.h]
, 正常的獲取這種資料結構的方式應該是先 vm.$data[time]
拿到 time 物件,然後再 time[h]
拿到 h 的值,所以我們需要對這種資料結構進行處理,為了已維護,我這裡單獨抽離了一個方法出來進行處理獲取 value,方法名字叫做 getValue,程式碼如下:
getValue(vm, value) {
// time.h --> [time, h]
return value.split('.').reduce((data, currentKey) => {
// 第一次執行: data=$data, currentKey=time
// 第二次執行: data=time, currentKey=h
return data[currentKey];
}, vm.$data);
},
reduce
方法被用於迭代這個字串陣列。它接受一個回撥函式,這個回撥函式在每次迭代中被呼叫。在這個回撥函式中,data 是上一次迭代的結果,而 currentKey 是當前迭代的陣列元素(鍵路徑中的一個部分)在每次迭代中,回撥函式透過 data[currentKey]
的方式訪問巢狀物件的屬性,然後將這個屬性的值作為下一次迭代的 data, 最終,reduce 方法將遍歷整個鍵路徑,直到達到最深層的屬性,然後返回該屬性的值。這樣我們就可以正常的獲取到資料了,最後在改造一下之前 model 方法獲取值的地方,呼叫下剛剛編寫的 getValue 方法即可:
model: function (node, value, vm) {
node.value = this.getValue(vm, value);
},
再次開啟瀏覽器,可以看到效果如下:
這個搞定之後,我們緊接著把 v-html 和 v-text 也搞定,程式碼基本上都是一樣的,只是渲染的方式不一樣,程式碼如下:
/**
* 處理 html 指令
* @param node 當前元素
* @param value 指令的值
* @param vm Nue 的例項物件
*/
html: function (node, value, vm) {
node.innerHTML = this.getValue(vm, value);
},
/**
* 處理 text 指令
* @param node 當前元素
* @param value 指令的值
* @param vm Nue 的例項物件
*/
text: function (node, value, vm) {
node.innerText = this.getValue(vm, value);
}
編寫測試程式碼:
html: `<div>我是div</div>`,
text: `<div>我是div</div>`
編寫HTML程式碼:
<div v-html="html">abc</div>
<div v-text="text">123</div>
開啟瀏覽器,可以看到效果如下: