还是两个文件,一个html,一个js
<body>
<div id="app">
<h1>{{str}}</h1>
<button @click="handlePrint">打印</button>
</div>
</body>
<script src="./Vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
str: "你好"
},
methods: {
handlePrint() {
this.str = "不好"
}
}
})
</script>
class Vue {
constructor(options) {
this.$options = options;
this.$watchEvent = {};
this.$data = options.data;
this.$el = document.querySelector(options.el);
// 数据劫持
this.proxyData()
// 数据监听
this.ovserve()
// 编译模板
this.compile(this.$el);
}
proxyData() {
// 1.给Vue实例赋熟悉,来自于data
// 2.data中的属性和Vue实例的属性是一一对应的(劫持)
for (let key in this.$data) {
Object.defineProperty(this, key, {
get() {
return this.$data[key];
},
set(val) {
this.$data[key] = val;
}
})
}
}
// 触发data中的数据发生变化来执行watch中的update
ovserve() {
for (let key in this.$data) {
let value = this.$data[key];
let that = this;
Object.defineProperty(this.$data, key, {
get() {
return value;
},
set(val) {
value = val;
if (that.$watchEvent[key]) {
that.$watchEvent[key].forEach((item, index) => {
item.update();
})
}
}
})
}
}
compile(node) {
node.childNodes.forEach((item, index) => {
// 判断节点类型
// 1===元素节点
if (item.nodeType === 1) {
if (item.hasAttribute("@click")) {
let vmKey = item.getAttribute("@click").trim();
item.addEventListener('click', (event) => {
this.eventFn = this.$options.methods[vmKey].bind(this)
this.eventFn(event);
})
}
if (item.childNodes.length > 0) {
this.compile(item);
}
}
// 2===属性节点
else if (item.nodeType === 2) { }
// 3===文本节点
else if (item.nodeType === 3) {
let reg = /\{\{(.*?)\}\}/g;
let text = item.textContent;
item.textContent = text.replace(reg, (match, key) => {
key = key.trim();
// 判断是否是data中的属性
if (this.hasOwnProperty(key)) {
let watcher = new Watche(this, key, item, "textContent")
// 检查当前对象是否已经有一个名为key的属性,用于存储观察者(watcher)列表
if (this.$watchEvent[key]) {
this.$watchEvent[key].push(watcher);
}
// 如果当前对象已经有一个名为key的属性,则将新创建的watcher添加到该属性中
else {
this.$watchEvent[key] = [];
this.$watchEvent[key].push(watcher);
}
}
return this.$data[key];
})
}
})
}
}
class Watche {
/**
* @function 构造函数
* @param {*} vm 对象
* @param {*} key 属性名称
* @param {*} node 节点
* @param {*} attr 改变文本节点内容的字符串
*/
constructor(vm, key, node, attr) {
this.vm = vm;
this.key = key;
this.node = node;
this.attr = attr;
}
update() {
// 更新文本节点的内容
this.node[this.attr] = this.vm.$data[this.key];
}
}