<head></head>
<body>
<div id="app">
<input id="editName" type="text">
<br />
name:<span id="name"></span>
<div>------------------------------</div>
<input id="editAge" type="text">
<br />
age:<span id="age"></span>
</div>
</body>
<script type="text/javascript">
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub);
},
notify: function () {
// 依次通知
this.subs.forEach(sub => {
sub.update();
})
}
}
// vm是vue实例
// exp指令的属性值 比如v-model='name'的name
// cb是更新函数
function Watcher(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get(); // 将自己添加到订阅器的操作
}
Watcher.prototype = {
update: function () {
var value = this.vm.data[this.exp]; //data.name 可能已经更改的数据
var oldVal = this.value; // 添加订阅者时初始化的数据
// 判断是否需要更新
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
},
get: function () {
Dep.target = this; // 缓存自己 (一个Watcher实例)
var value = this.vm.data[this.exp] // 如selfVue.data['name'] 触发Observe(defineProxy)中的get,收集依赖
Dep.target = null; // 释放自己 由于target是Dep的原型变量,以免影响其他观察者
return value;
}
};
function defineProxy(obj, key, value) {
// 递归所有子属性
Observe(value);
// 对于每一个实例,都有一个订阅者数组
let dep = new Dep();
// 每调用一次defineProxy,就产生一个该函数上下文,保存这个val
let val = value;
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
set(newVal) {
if (val === newVal) {
return;
}
console.log(`${val}=>${newVal}`)
val = newVal;
dep.notify(); //通知订阅者
},
get() {
// 在Watcher.get()中触发到这里
if (Dep.target) {
// 把该watcher加到订阅者中
dep.addSub(Dep.target)
console.log(dep.subs)
}
return val;
}
})
}
function Observe(data) {
if (!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(function (key) {
defineProxy(data, key, data[key]);
});
}
function SelfVue(options) {
let data = options.data
this.data = data
Observe(this.data);
Object.keys(data).forEach(key => {
let el = document.getElementById(key) || null
// 还要判断是什么类型的dom , 根据不同的dom去设置不同的回调函数
if (el) {
el.innerHTML = data[key] // 初始化
// 为该数据创建一个Watcher
// 一个数据/dom节点对应的应该就是一个watch对象
new Watcher(this, key, function (value,oldValue) {
el.innerHTML = value;
});
}
})
return this;
}
var selfVue = new SelfVue({
el: '#app',
data: {
name: 'jenson',
age: 20
}
});
let inputNameDom = document.getElementById('editName')
inputNameDom.oninput = (e) => {
selfVue.data.name = inputNameDom.value; //触发data的set拦截器
}
let inputAgeDom = document.getElementById('editAge')
inputAgeDom.oninput = (e) => {
selfVue.data.age = inputAgeDom.value; //触发data的set拦截器
}
</script>
重温Vue数据绑定原理(幼儿园版)
最新推荐文章于 2023-06-25 15:20:25 发布