Observer进行数据劫持,Dep进行依赖收集,Watcher进行页面数据变化的监听,Compile对不同结点进行处理,初始化模板,更新模板等
<!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>
<style type="text/css"></style>
</head>
<body>
<div id="app">
<input type="text" v-model="text" />
{{text}}
</div>
<script>
class Vue {
constructor(options) {
this.$options = options;
// 对数据进行劫持
new Observer(this.$options.data);
// console.log(this.$options.data);
let dom = document.querySelector(this.$options.el);
// 返回新的经过parse的文档碎片
const f = new Compile(dom, this.$options.data);
// 将f再添加到dom中
dom.append(f);
}
}
class Observer {
constructor(data) {
this.walk(data);
}
walk(data) {
Object.keys(data).forEach((key) => {
let value = data[key];
if (typeof value === "object") {
walk(value);
} else {
this.defineReactive(data, key, value);
}
});
}
defineReactive(data, key, value) {
let dep = new Dep();
Object.defineProperty(data, key, {
get() {
// 这里进行依赖收集,由于这儿获取不到node结点,只能在Dep上添加一个对象指向watcher自身
if (Dep.target) {
dep.addSub(Dep.target);
}
return value;
},
set(newVal) {
if (value === newVal) return;
value = newVal;
dep.notify();
},
});
}
}
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push(watcher);
}
// 通知每个watcher进行更新
notify() {
this.subs.forEach((watcher) => {
watcher.update();
});
}
}
class Watcher {
constructor(vm, key, callback) {
Dep.target = this;
// console.log("watch--------");
this.vm = vm;
this.key = key;
this.callback = callback;
this.oldVal = "";
this.update();
Dep.target = null;
}
getNewVal() {
// 监听Data里的值
return this.vm[this.key];
}
update() {
let newVal = this.getNewVal();
if (this.oldVal !== newVal) {
// console.log(newVal, this.oldVal);
this.callback(newVal);
this.oldVal = newVal;
}
}
}
class Compile {
constructor(node, vm) {
this.node = node;
// vm就是option里的data
this.vm = vm;
return this.nodeList(this.node);
}
// 对结点进行处理
parse(node, vm) {
// console.log(node); // 这里有三个元素:换行/n,input标签,文本数据{text}
let reg = /\{\{(.*)\}\}/; // 正则获取文本数据{text}里的内容
// 这里node就是input标签
if (node.nodeType === 1) {
Array.from(node.attributes).forEach((v) => {
// v是input标签上的属性
if (v.nodeName === "v-model" && node.nodeName === "INPUT") {
// // 初始化input里的文本值
node.value = vm[v.nodeValue];
// 绑定事件监听文本框里值的变化
node.addEventListener("input", (e) => {
// 这里的v.nodeValue就是v-model绑定的text,也就是对options里的data进行修改
vm[v.nodeValue] = e.target.value;
console.log(vm);
});
}
});
}
// 获取{text}文本
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
// 初始化和更新时添加一个监视器,并指定回调函数
new Watcher(vm, RegExp.$1, (newVal) => {
node.nodeValue = newVal;
});
}
}
}
// 遍历nodeList对每个结点进行处理
nodeList(node) {
let f = document.createDocumentFragment();
let child;
// 每次文档碎片进行添加时会移除node里的一个子节点,这样不会无限循环
while ((child = node.firstChild)) {
this.parse(child, this.vm);
f.append(child);
}
return f;
}
}
let vue = new Vue({
el: "#app",
data: {
text: "hello Vue",
},
});
</script>
</body>
</html>