數(shù)據(jù)驅(qū)動
Vue.js 的核心思想就是數(shù)據(jù)驅(qū)動葛闷。那么什么是數(shù)據(jù)驅(qū)動呢? 數(shù)據(jù)驅(qū)動就是視圖由數(shù)據(jù)決定放案,數(shù)據(jù)作為主動。如我們談到TDD 測試驅(qū)動開發(fā)呐舔,也就是以測試為主動開發(fā)币励。只要我們定義好了視圖與數(shù)據(jù)的關系,那么我們前端開發(fā)人員就可以更專注數(shù)據(jù)珊拼。無需花更多時間在數(shù)據(jù)更新后如何去更新視圖食呻。接下我們就去實現(xiàn)一個通過模板將數(shù)據(jù)生成為 DOM 的過程。其實我們先研究的就是所謂單向綁定澎现,隨后我們再談雙向綁定仅胞。
<div id="app">
<div>{{message}}</div>
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
上面的代碼可能是我們在學習 vuejs 時,第一個接觸的 vue 的代碼剑辫,實現(xiàn)第一個 vue 應用干旧, 用 data 的 message 來替換 {{message}}
。
下面我們用代碼來實現(xiàn)一下上面功能妹蔽,我們先將注意力放置如何解析模板獲取占位符椎眯,然后將其進行替換挠将。
const bracketReg = /\{\{(.+?)\}\}/g
const appNode = document.querySelector("#app");
let data = {
message: "hello zidea"
}
首先我們需要得到 dom 元素,可以通過 querySelector 獲取 dom 元素编整,querySelector 讓我們遠離 jquery舔稀,雖然還是很懷念 jquery 給我們帶來驚喜,但是昨天畢竟已經(jīng)是昨天了掌测。別來 jquery 了内贮,雖然時常還會不知不覺用到他。
const copyAppNode = appNode.cloneNode(true)
因為我們節(jié)點是引用類型赏半,所以我們使用 cloneNode(ture)復制出一份贺归,然后在復制出節(jié)點上進行操作。然后就開始寫 compile 函數(shù)断箫,compile 接收 dom 元素和數(shù)據(jù) data 作為參數(shù)拂酣,然后通過正則表達式將 textNode 的占位符進行替換。
function compile(template, data) {
let children = template.childNodes;
//NodeList(3) [text, div, text]
children.forEach(element => {
let type = element.nodeType;
if (type == 3) { // text node case
let content = element.nodeValue;
console.log(content)
content = content.replace(bracketReg, function (_, g) {
// console.log(g)
const key = g.trim()
const value = data[key]
return value
})
element.nodeValue = content;
} else if (type == 1) {
compile(element, data);
}
});
}
然后調(diào)用函數(shù) compile 對dom 元素的中文本進行替換仲义,過程這里可以理解為編譯婶熬,然后編譯后的節(jié)點替換原有的節(jié)點。這里用了需要 native js 方法埃撵,大家平時寫多了 react 赵颅、vue 可能都很少寫 native js 除了處理一些邏輯時會用到 native js。
compile(copyAppNode, data);
console.log(app)
app.parentNode.replaceChild(app, copyAppNode)
const bracketReg = /\{\{(.+?)\}\}/g
function compile(template, data) {
let children = template.childNodes;
//NodeList(3) [text, div, text]
children.forEach(element => {
let type = element.nodeType;
if (type == 3) { // text node case
let content = element.nodeValue;
console.log(content)
content = content.replace(bracketReg, function (_, g) {
// console.log(g)
const key = g.trim()
const value = data[key]
return value
})
element.nodeValue = content;
} else if (type == 1) {
compile(element, data);
}
});
}
接下來我們工作就是暂刘,整理代碼將將我們代碼寫的更像點 vue
class Zig {
constructor(options) {
this._el = options.el;
this._data = options.data;
this.$el = this._templateDOM = document.querySelector(this._el);
this._parent = this._templateDOM.parentNode;
//將數(shù)據(jù)渲染到界面上
this.render();
}
//將模板和數(shù)據(jù)編譯為 html
render() {
this.compile();
}
//編譯將模板和數(shù)據(jù)結(jié)合得到真正的 DOM
compile() {
let htmlDOM = this._templateDOM.cloneNode(true);
compile(htmlDOM, this._data);
this.update(htmlDOM);
}
//將 DOM 掛載到頁面的元素
update(htmlDOM) {
this._parent.replaceChild(htmlDOM, document.querySelector("#app"))
}
}
const app = new Zig({
el: "#app",
data: {
message: "hello zidea"
}
})
這里為什么叫 Zig 呢? Zi 源于 zidea 而 g 源于 go饺谬,也就是這個我這個 zig 框架部門代碼是通過 go 寫 wasm 來提供性能,所以叫 Zig