!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<input v-model="text" type="text" />
{{text}}
</div>
</body>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
<script>
//observer 数据的劫持
// wathcer 依赖侦听器
// dep 依赖收集
// compile 编译dom
class Vue {
constructor(options) {
//将new Vue 的参数传入
this.$options = options; // {el: '#app', data: {…}}
new Observer(this.$options.data);
//把跟节点传入
let dom = document.querySelector(this.$options.el);
const f = new Compile(dom, this.$options.data);
dom.append(f);
}
}
class Observer {
//接收一个data
constructor(data) {
this.walk(data);
}
walk(data) {
//数据劫持
Object.keys(data).forEach((key) =>
this.defineReactive(data, key, data[key])
);
}
defineReactive(data, key, value) {
let dep = new Dep();
Object.defineProperty(data, key, {
get() {
//添加watcher 实例
if (Dep.target) {
// console.log(Dep.target);
dep.addSub(Dep.target);
}
return value;
},
set(newValue) {
value = newValue;
dep.notify();
},
});
}
}
class Dep {
constructor() {
this.sub = [];
}
addSub(wathcer) {
//push watcher的实例子
this.sub.push(wathcer);
}
notify() {
this.sub.forEach((wathcer) => wathcer.update()); //更新值
}
}
class Wathcer {
constructor(vm, node, name) {
Dep.target = this;
this.vm = vm;
this.node = node;
this.name = name;
this.update(); //初始化赋默认值
Dep.target = null; //销毁防止重复添加
}
get() {
//取值
this.value = this.vm[this.name];
}
update() {
this.get();
this.node.nodeValue = this.value; //更新花括号内容
}
}
class Compile {
//一个node 一个vm
constructor(node, vm) {
this.node = node;
this.vm = vm;
return this.nodeList(this.node); // 不知道为啥return
}
parse(node, vm) {
let reg = /\{\{(.*)\}\}/;
//nodeType node 节点13种
if (node.nodeType === 1) {
//获取v-model
//转为真数组
// console.log(Array.from(node.attributes));
Array.from(node.attributes).forEach((v) => {
if (v.nodeName === "v-model") {
//添加input事件 更新nodeValue
node.addEventListener("input", (e) => {
// v.nodeValue 是 data下的text
vm[v.nodeValue] = e.target.value;
});
// node 为 input 框
node.value = vm[v.nodeValue]; //绑定初始值
}
});
}
if (node.nodeType === 3) {
//要收集依赖 监听 更新节点
// console.log(node);
if (reg.test(node.nodeValue)) {
//vm[RegExp.$1] 为 data中的 ‘text’
// console.log(vm[RegExp.$1]);
node.nodeValue = vm[RegExp.$1];
//收集依赖
new Wathcer(vm, node, RegExp.$1);
}
//处理花括号
}
}
nodeList(node) {
//创建文档碎片
let f = document.createDocumentFragment();
let child;
//传入根元素节点
while ((child = node.firstChild)) {
this.parse(child, this.vm);
f.appendChild(child); //不添加文档碎片会死循环
}
return f;
}
}
new Vue({
el: "#app",
data: {
text: "text",
},
});
</script>
</html>
vue2手写实现双向数据绑定(面试)
最新推荐文章于 2024-04-06 20:51:26 发布