<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
// 第一步 实现基本架构 MVVM
// 第二步 把模型的数据 显示到视图中 M -> V
// 第三步 更新视图同模型 再更新视图 V -> M
class Vue {
constructor(options) {
this.options = options;
this.$data = options.data; // 获取数据
this.$el = document.querySelector(options.el); // 获取对象
this._directives = {}; // 存放订阅者
// {myText: [订阅者1,订阅者2], myBox: [订阅者3,...]}
this.Observer(this.$data);//
this.Compile(this.$el);
}
// 劫持数据
Observer(data){
for(let key in data){
this._directives[key] = [];
let Val = data[key]; // 当前的值
let watch = this._directives[key]; // 得到对应watcher的数组
// this.$data 对象中的每一个属性 发生赋值 都要更新视图
Object.defineProperty(this.$data, key, {
get: function () {
return Val;
},
set: function (newVal) {
if(newVal !== Val){
Val = newVal;
// 遍历订阅者 通知更新视图
watch.forEach(function(item){
item.update();
})
}
}
})
}
}
// 解析指令
Compile(el){
let nodes = el.children; // 获取APP下面的子元素
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i]; // 当前元素
if(node.children.length > 0) {
this.Compile(node); // 递归循环子节点
}
if(node.hasAttribute("v-text")){
let attrVal = node.getAttribute("v-text");
//node.innerHTML = this.$data[attrVal];
this._directives[attrVal].push(new Watcher(node, attrVal, this, "innerHTML"));
}
if(node.hasAttribute("v-model")){
let attrVal = node.getAttribute("v-model");
//node.value = this.$data[attrVal];
this._directives[attrVal].push(new Watcher(node, attrVal, this, "value"));
node.addEventListener("input",() => {
this.$data[attrVal] = node.value;
})
}
}
}
}
// 订阅者 更新视图
class Watcher {
constructor(el, vm, mySelf, attr){
this.el = el;
this.vm = vm;
this.mySelf = mySelf;
this.attr = attr;
this.update(); // 初始化数据
}
update(){
this.el[this.attr] = this.mySelf.$data[this.vm];
}
}
</script>
</head>
<body>
<div id="app">
<h1>数据响应式</h1>
<div>
<div v-text="myText"></div>
<div v-text="myBox"></div>
<input type="text" v-model="myText">
</div>
</div>
<script>
const app = new Vue({
el: "#app",
data: {
myText: "hello world!",
myBox: "Vue.js",
}
})
</script>
</body>
</html>
学习笔记