<!DOCTYPE html>
<html lang="zh-CN">
<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>
<!-- 实现vue -->
<div id="app">
<input type="text" v-model="text"> {{ text }}
</div>
<script>
// 设置一个监听器
function observe(obj, vm) {
for (let key of Object.keys(obj)) {
defineReactive(vm, key, obj[key])
}
};
function defineReactive(obj, key, val) {
let dep = new Dep();
Object.defineProperty(obj, key, {
get: function() {
if (Dep.target) {
dep.addSub(Dep.target)
}
return val
},
set: function(newVal) {
if (newVal === val) {
return
} else {
val = newVal;
dep.notify();
}
}
})
};
// 设置一个编译器
function compile(node, vm) {
if (node.nodeType === 1) {
let attr = node.attributes;
for (let i = 0; i < attr.length; i++) {
if (attr[i].nodeName === "v-model") {
let name = attr[i].nodeValue;
node.addEventListener("input", function(e) {
vm[name] = e.target.value
});
node.value = vm[name];
node.removeAttribute("v-model");
}
}
};
let reg = /\{\{(.*)\}\}/;
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
let name = RegExp.$1;
name = name.trim();
// node.nodeValue == vm[name]
new Watcher(vm, node, name);
}
}
};
// 定义订阅者构造函数
function Watcher(vm, node, name) {
Dep.target = this;
this.vm = vm;
this.node = node;
this.name = name;
this.update();
Dep.target = null;
};
Watcher.prototype = {
get() {
this.value = this.vm[this.name]
},
update() {
this.get();
this.node.nodeValue = this.value;
}
};
// 定义订阅器构造函数
function Dep() {
this.subs = []
};
Dep.prototype = {
addSub(sub) {
this.subs.push(sub);
},
notify() {
this.subs.forEach(function(sub) {
sub.update();
})
}
};
// 模板处理
function nodeToFragment(node, vm) {
let fragment = document.createDocumentFragment();
let child;
while (child = node.firstChild) {
compile(child, vm);
fragment.appendChild(child)
};
return fragment
};
// vue初始化
function Vue(options) {
this.data = options.data;
let data = this.data;
observe(data, this);
let id = options.el;
let dom = nodeToFragment(document.getElementById(id), this);
document.getElementById(id).appendChild(dom)
};
const vm = new Vue({
el: "app",
data: {
text: "hello world"
}
})
</script>
</body>
</html>
手写vue双向绑定原理
最新推荐文章于 2024-03-02 12:03:40 发布