VUE对象的创建
class Vue {
constructor(options) {
this.$el = document.querySelector(options.el);
this.$options = options;
this.$watchEvent = {};
if (typeof this.$options.beforeCreate == "function") {
this.$options.beforeCreate.call(this);
}
this.proxyData();
this.proxyMethods();
if (typeof this.$options.created == "function") {
this.$options.created.call(this);
}
if (typeof this.$options.beforeMount == "function") {
this.$options.beforeMount.call(this);
}
this.compile(this.$el);
if (typeof this.$options.mounted == "function") {
this.$options.mounted.call(this);
}
}
proxyData() {
for (const key in this.$options.data) {
let value = this.$options.data[key];
Object.defineProperty(this, key, {
configurable: false,
enumerable: true,
get() {
return value;
},
set(val) {
value = val;
if (this.$watchEvent[key]) {
this.$watchEvent[key].forEach((item, i) => {
item.update(this);
});
}
},
});
}
}
compile(DOMnode) {
DOMnode.childNodes.forEach((node, index) => {
if (node.nodeType == 3) {
let value = node.textContent;
let reg = /\{\{(.*?)\}\}/;
node.textContent = value.replace(reg, (match, val) => {
let watcher = new Watcher(this, val, node, "textContent");
if (this.$watchEvent[val]) {
this.$watchEvent[val].push(watcher);
} else {
this.$watchEvent[val] = [];
this.$watchEvent[val].push(watcher);
}
return this[val];
});
} else if (node.nodeType == 1) {
var VueEvents = ["v-html", "v-model"];
VueEvents.forEach((event, i) => {
if (node.hasAttribute(event)) {
var vmKey = node.getAttribute(event).trim();
var watcher;
switch (event) {
case "v-html":
watcher = new Watcher(this, vmKey, node, "innerHTML");
node.innerHTML = this[vmKey];
break;
case "v-model":
if (this.hasOwnProperty(vmKey)) {
watcher = new Watcher(this, vmKey, node, "value");
node.value = this[vmKey];
node.addEventListener("input", (e) => {
this[vmKey] = e.target.value;
});
}
break;
}
if (this.$watchEvent[vmKey]) {
this.$watchEvent[vmKey].push(watcher);
} else {
this.$watchEvent[vmKey] = [];
this.$watchEvent[vmKey].push(watcher);
}
if (node.hasAttribute("@click")) {
node.addEventListener("click", (e) => {
this[node.getAttribute("@click")].call(this, e);
});
}
node.removeAttribute(event);
}
if (node.childNodes.length != 0) {
this.compile(node);
}
});
}
});
}
proxyMethods() {
for (const key in this.$options.methods) {
let method = this.$options.methods[key];
this[key] = method;
}
}
}
class Watcher {
constructor(vm, key, node, attr) {
this.vm = vm;
this.key = key;
this.node = node;
this.attr = attr;
}
update(vm) {
if (typeof vm.$options.beforeUpdate == "function") {
vm.$options.beforeUpdate.call(this);
}
this.node[this.attr] = this.vm[this.key];
if (typeof vm.$options.updated == "function") {
vm.$options.updated.call(this);
}
}
}
在模板中使用VUE对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{value}}</p>
<input type="text" v-model="value" />
<p v-html="value"></p>
<button @click="changeValue">changeValue</button>
</div>
<script src="./myVue.js"></script>
<script>
let vm = new Vue({
el: "#app",
data: {
value: "",
},
beforeCreate() {
console.log(`开始渲染数据,value=${this.value}`);
},
created() {
this.changeValue();
console.log(`数据渲染完毕,value=${this.value}`);
},
beforeMount() {
console.log(`开始编译dom`);
},
mounted() {
console.log(`dom编译完毕`);
},
beforeUpdate() {
console.log("马上要更新dom啦");
},
updated() {
console.log("dom更新完啦");
},
methods: {
changeValue() {
this.value = "321";
},
},
});
</script>
</body>
</html>