v-model双向数据绑定底层原理代码
程序员文章站
2022-06-19 12:51:14
<!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">
<input type="text" v-model="inputValue" />
<input type="text" />
</div>
<script>
// 观察者模式 : 发布者 订阅者
class Dep {
constructor() {
// 订阅者数组
this.watchers = [];
}
// 添加订阅者
addWatcher(watcher) {
console.log("添加了:" + watcher);
this.watchers.push(watcher);
}
notify(newValue) {
console.log(this.watchers);
this.watchers.forEach((w) => {
w.upload(newValue);
});
}
}
// 1. const dep = new Dep()
// 2. const w1 = new Wathcer((v) => {console.log(v)});
// 3. dep.addWatcher(w1)
// 4. dep.notify('我发布了新文章')
class Watcher {
constructor(data, key, callback) {
this.callback = callback;
// 我们劫持的是 inputValue这个属性 我调用一个 data.inputValue
Dep.target = this;
this.value = data[key];
console.log("watcher的构造函数:" + this.value);
}
upload(newValue) {
this.callback(newValue);
}
}
// 接下来 我们自己来实现一个Vue
// 1. 创建一个类
class MyVue {
// 只要外界一旦new,就会执行里面的 constructor方法
constructor({ el, data }) {
// 1. 我先获取到这个容器,绑定在当前实例对象上面
this.container = document.querySelector(el);
// 2. 把当前的data里面的数据 也绑定在 当前实例上面
this.data = data;
this.vmData();
this.initInputData();
}
// 2 我需要把data里面的 inputValue的值显示在 页面的input元素上面
initInputData() {
// 记录一下当前的this
const _this = this;
// 我要做的事情是 只要有 v-model属性的input元素,要默认显示 data里面的属性
const models = this.container.querySelectorAll("[v-model]");
// 遍历这个数组
models.forEach((element) => {
// 1. 获取到 v-model里面的值,因为这个值就是 data对象里面的属性名
const attr = element.getAttribute("v-model");
element.value = this.data[attr];
// 视图的更新来带动 数据的更新 ,如何知道视图更新了? input事件
element.oninput = function () {
// 只需要在这个地方来进行赋值就好
// 我们data数据是绑定在 vm实例上的
// 这个函数里面this指向 input
_this.data[attr] = this.value;
};
const watcher = new Watcher(this.data, attr, (newValue) => {
element.value = newValue;
});
});
}
// 数据驱动视图的话 数据劫持 + 观察者模式
// 观察者 监听数据的变化,只要数据变化了,我就通知视图的更新
vmData() {
// 首先要做的是 数据劫持
// Object.defineProperty()
const entries = Object.entries(this.data);
// entries 可以得到对象里面所有的属性,二维数组,数组里面的第一项代表的是 属性名,第二项代表的是属性值
entries.forEach((en) => {
const dep = new Dep();
let rectiveValue = en[1];
// en 是一个数组 ["inputValue","默认值"]
Object.defineProperty(this.data, en[0], {
set(newValue) {
rectiveValue = newValue;
console.log("劫持到了数据:" + newValue);
// 只要监听到数据变化,就应该通知 外界
dep.notify(newValue);
},
get() {
console.log(Dep.target);
Dep.target && dep.addWatcher(Dep.target);
console.log("获取到了数据:" + rectiveValue);
return rectiveValue;
},
});
});
}
}
const vm = new MyVue({
el: "#app",
data: {
inputValue: "默认值",
inputValue1: "默认值1",
inputValue2: "默认值2",
},
});
</script>
</body>
</html>
本文地址:https://blog.csdn.net/X_Flandre/article/details/109238467