經過上一篇的介紹,資料驅動介面改變 v-model 的雙向繫結已告一段落, 剩餘的就以這篇文章來完成。
首先完成我們的 v-html,v-text, 其實很簡單,就是將我們之前的 v-model 建立觀察者的方法,在 v-html 和 v-text 中再寫一次即可,建立屬於 v-html 和 v-text 的觀察者。
v-html:
html: function (node, value, vm) {
new Watcher(vm, value, (newValue, oldValue) => {
node.innerHTML = newValue;
});
node.innerHTML = this.getValue(vm, value);
},
v-text:
text: function (node, value, vm) {
new Watcher(vm, value, (newValue, oldValue) => {
node.innerText = newValue;
});
node.innerText = this.getValue(vm, value);
},
測試一下 v-html,開啟瀏覽器控制檯,輸入 vue.$data.html = '<p>我是測試v-html<p/>'
,可以看到介面上的內容已經改變了。
測試一下 v-text,開啟瀏覽器控制檯,輸入 vue.$data.text = '<p>我是測試v-text<p/>'
,可以看到介面上的內容已經改變了。
好了到此為止,指令的資料驅動介面改變已經完成了,接下來我們來完成模板語法的資料驅動介面改變。
這個就與之前的指令的資料驅動介面改變不同了,好了先不說問題,我們先直接來看程式碼一步一步分析。
我看了下之前處理 content 的程式碼發現,獲取不到對應的屬性名稱叫什麼,因為是直接呼叫 this.getContent(vm, value); 獲取的,所以會出現一個問題就是給 content 建立觀察者物件的時候不能直接告訴他我要監聽的是哪個屬性,所以我就想到了一個辦法。
首先將之前的程式碼註釋掉,再然後我編寫一個正規表示式,關於這個正規表示式在之前的文章中有講到,大概意思就是匹配 {{}}
中的內容,這裡就不再贅述了。
let reg = /\{\{(.+?)\}\}/gi;
繼續往下看,我利用 value 呼叫了 replace 方法,傳遞了兩個引數,第一個引數是剛剛編寫的正規表示式,第二個引數是一個函式,這個函式的作用就是將匹配到的內容替換成對應的值,我先將其返回值列印出來,看看是什麼,我們的程式碼就可以寫成這樣。
content: function (node, value, vm) {
// console.log(value); // {{ name }} -> name -> $data[name]
// node.textContent = this.getContent(vm, value);
let reg = /\{\{(.+?)\}\}/gi;
value.replace(reg, (...args) => {
console.log(args[1].trim());
});
}
是的,我們的確獲取到了對應的屬性名稱,接下來我們就可以利用這個屬性名稱來建立觀察者物件了,我們的程式碼就可以寫成這樣。
content: function (node, value, vm) {
// console.log(value); // {{ name }} -> name -> $data[name]
// node.textContent = this.getContent(vm, value);
let reg = /\{\{(.+?)\}\}/gi;
node.textContent = value.replace(reg, (...args) => {
const attr = args[1].trim();
new Watcher(vm, attr, (newValue, oldValue) => {
node.textContent = this.getContent(vm, value);
});
return this.getValue(vm, args[1]);
});
}
好了,我們來測試一下,開啟瀏覽器控制檯,輸入 vue.$data.name = '我是測試 {{ name }}'
,可以看到介面上的內容已經改變了。
一切看起來都很完美,最終版程式碼其實是我沒有將坑點說出來,現在我們來看看這個坑點是什麼,再看之前,我來講述一下為什麼是又呼叫了 this.getContent 方法而不是直接將 newValue 賦值給 node.textContent。
假如我們的資料結構是這樣的 {{ name }} - {{ age }}
如果是透過直接將 newValue 賦值給 node.textContent 的話,這個時候呢,我假設 name 的值是 BNTang, age 的值是 33,那麼介面上第一次載入的內容會是 BNTang - 33,但是如果我將 name 的值改成了 xhh,那麼介面上的內容就會變成 xhh,這個時候 age 的值就丟掉了,如下圖是我的測試結果。
原因就是直接替換掉了,所以在動態更改 name 屬性或者 age 屬性其中一個的情況下,還需要將 {{ name }} - {{ age }}
這樣的內容替換成 BNTang - 33,這樣的話,我們就需要呼叫 this.getContent 方法,這個方法就會利用正則挨個匹配 {{}}
中的內容,然後再將其替換成對應的值,這樣就不會出現上面的問題了。
?> 最後總結一下 content 函式的 value.replace 在外層是為了拿到屬性名稱,內層是為了保證資料完整性。