数据驱动
Vue.js 的核心思想就是数据驱动。那么什么是数据驱动呢? 数据驱动就是视图由数据决定,数据作为主动。如我们谈到TDD 测试驱动开发,也就是以测试为主动开发。只要我们定义好了视图与数据的关系,那么我们前端开发人员就可以更专注数据。无需花更多时间在数据更新后如何去更新视图。接下我们就去实现一个通过模板将数据生成为 DOM 的过程。其实我们先研究的就是所谓单向绑定,随后我们再谈双向绑定。
<div id="app">
<div>{{message}}</div>
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
上面的代码可能是我们在学习 vuejs 时,第一个接触的 vue 的代码,实现第一个 vue 应用, 用 data 的 message 来替换 {{message}}
。
下面我们用代码来实现一下上面功能,我们先将注意力放置如何解析模板获取占位符,然后将其进行替换。
const bracketReg = /\{\{(.+?)\}\}/g
const appNode = document.querySelector("#app");
let data = {
message: "hello zidea"
}
首先我们需要得到 dom 元素,可以通过 querySelector 获取 dom 元素,querySelector 让我们远离 jquery,虽然还是很怀念 jquery 给我们带来惊喜,但是昨天毕竟已经是昨天了。别来 jquery 了,虽然时常还会不知不觉用到他。
const copyAppNode = appNode.cloneNode(true)
因为我们节点是引用类型,所以我们使用 cloneNode(ture)复制出一份,然后在复制出节点上进行操作。然后就开始写 compile 函数,compile 接收 dom 元素和数据 data 作为参数,然后通过正则表达式将 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);
}
});
}
然后调用函数 compile 对dom 元素的中文本进行替换,过程这里可以理解为编译,然后编译后的节点替换原有的节点。这里用了需要 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;
//将数据渲染到界面上
this.render();
}
//将模板和数据编译为 html
render() {
this.compile();
}
//编译将模板和数据结合得到真正的 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