实现vue 的双向绑定实现 从数据到视图,和从视图到数据
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<div id="id">
<h1>{{ww}}</h1>
<h1>{{ww}}</h1>
<input v-model="ww">
</div>
</head>
<body>
<script>
var ec = new guan()//创建一个观察者
//主构造器
function mvvm(options) {
let { data, el } = options //对象解构赋值
// this.data = options.data
for (let key in data) {
Object.defineProperty(this, key, {
set(val) {
if (val === data[key]) { //如果设置的值和以前的值相等,就返回
return
}
console.log('开始设置');
data[key] = val
ec.emit(key)//设置观察者
},
get() {
console.log('我是假的');
return data[key]
}
})
}
new Compiler(el, this)
}
// 获取dom 构造器
function Compiler(el, vm) {
this.vm = vm
this.dom = document.querySelector(el)
console.log(this.dom, this.vm);
this.compile(this.dom)
}
Compiler.prototype.compile = function (el) {
// el就是获取到的元素节点
// console.log(el.childNodes);//显示所有的子节点 以数组形式
el.childNodes.forEach(node => {//遍历获取到的节点
if (node.nodeType === 3) {//说明是文本节点
//调用解析文本节点的方法
// console.log(node);
this.compileText(node)
} else if (node.nodeType === 1) { //说明是元素节点
//元素节点的方法
this.compileElment(node)
// console.log(node);
//元素节点中可能含有文本节点,再进行遍历
console.log(node);
this.compile(node)//利用递归,找出所有的文本节点
}
})
}
Compiler.prototype.compileText = function (dom) { //处理文本节点 显示数据 {{}}解析其中的值
console.log('我是文本节点' + dom);
var shou = []//收集要观察的所有属性名
var str = dom.textContent//取出文本节点的内容
var newStr = str.replace(/{{(.+?)}}/g, (objstr, p1) => {
// console.log('看我'+objstr,p1,this.vm[p1]);
shou.push(p1)
return this.vm[p1] //返回vm的数值 用来替换 p1为ww
});
dom.textContent = newStr //此时newStr为1000
// 添加观察者
shou.forEach(key => {
// 给事件中心添加事件
ec.add(key, () => {
console.log(`${key},变化了`)
// 1. 取出文本节点的内容 str
// 2. 通过正则替换
// 一定要使用 =>
var newStr = str.replace(/{{(.+?)}}/g, (objstr, p1) => {
return this.vm[p1]
});
// 3. 显示替换的结果
dom.textContent = newStr
})
})
}
Compiler.prototype.compileElment = function (dom) { //处理元素节点 v-model 中的值 替换为具体数值
console.log('我是元素节点' + dom);
if (dom.hasAttribute('v-model')) {//判断是否有v-model属性
var key = dom.getAttribute('v-model')//取出该属性 key为 ww
dom.value = this.vm[key] //将属性值替换为 实际值
// 添加监听者
// 当key属性发生变化时,去更新input框中的值
ec.add(key, () => {
dom.value = this.vm[key]
})
// 给元素本身添加input事件 从视图到 数据的修改
dom.addEventListener("input", (e) => {
// console.dir(e)
// 1. 获取当前用户改完之后的值
console.log(e.target.value)
// 2. 设置给对象的属性值(数据)
// 会进入到set拦截器,发布消息
this.vm[key] = e.target.value
console.log("用户在input中做了修改")
})
}
}
//引入观察者
function guan() {
this.da = {}
}
guan.prototype.add = function (ming, zhi) {
var ll = this.da[ming]
// this.da[ming].push(zhi)
if (!ll) {//如果没有这个值,就创建这个值再t添加
this.da[ming] = []
this.da[ming].push(zhi)
}
else {
ll.push(zhi)
}
}
guan.prototype.emit = function (ming) {
var tt = this.da[ming]
if (tt) {
this.da[ming].forEach(item => {
item()
})
}
}
</script>
<script>
var data = {
ww: 1000,
ff: 444
}
var vm = new mvvm({
el: '#id',
data
})
</script>
</body>
</html>