vue 数据双向绑定 实现逻辑
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title></title>
</head>
<body>
<div id="app"><input type="text" v-model="text" /> {{text}}</div>
</body>
<script src="./Mvvm.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: 'app',
data: {
text: '超哥哥',
},
})
vm.data.text = '超哥哥To'
</script>
</html>
js
class Vue {
constructor(options) {
this.data = options.data //创建data 数据
this.optionsTo = options
this.observe(this.data, this) //对数据数据进行双向绑定
let id = options.el // 获取挂载点
let dom = this.nodeToFrament(document.getElementById(id), this) //创建文档碎片,并处理
document.getElementById(id).appendChild(dom) //将处理好的文档碎片添加到 文档中
}
nodeToFrament(node, vm) {
let fragment = document.createDocumentFragment() //创建文档碎片
let child
while ((child = node.firstChild)) {
// 这里是一个判断条件条件,当node.firstChild 为null 的时候会停下来
this.compile(child, vm) //执行对 节点的处理
fragment.appendChild(child) // 将 处理完的数据添加到 文档碎片容器中
}
return fragment
}
compile(node, vm) {
let reg = /\{\{(.*)\}\}/ //创建去除双花括号的 正则
if (node.nodeType === 1) {
//如果 节点类型等于 1, 那么代表是 元素节点
let attr = node.attributes //拿到 元素节点的所有属性
for (let i = 0; i < attr.length; i++) {
if (attr[i].nodeName == 'v-model') {
//如果 元素节点中出现 v-model 属性
let name = attr[i].nodeValue //拿到 属性对应的 值
node.addEventListener('input', function (e) {
vm.data[name] = e.target.value
})
new Watcher(vm, node, name)
// node.value = vm.data[name] //去data 里面查找对应的 数据并赋值给对应 元素
node.removeAttribute('v-model')
}
}
}
if (node.nodeType === 3) {
//如果是 文本节点
if (reg.test(node.nodeValue)) {
let name = RegExp.$1 //调用正则
name = name.trim() //去掉前后空格
// node.nodeValue = vm.data[name] //去data 里面查找对应的 数据并赋值给对应 元素
new Watcher(vm, node, name) //实例化Watcher 监听数据
}
}
}
observe(obj) {
if (!obj || typeof obj !== 'object') {
return
} else {
Object.keys(obj).forEach((key) => {
this.defneReactive(obj, key, obj[key])
})
}
}
defneReactive(obj, key, val) {
let dep = new Dep() //Dep 函数相当于是一个中间件,桥梁
this.observe(val)
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target) //只要每次获取数据 都将全局变量里面的数据存储到dep 里面,以便设置数据时调用
}
return val
},
set(newVal) {
if (newVal === val) {
return
} else {
val = newVal
dep.notify() //触发notify 方法,dep 里面的数据呈现到页面
}
},
})
}
}
class Dep {
//Dep 函数相当于是一个中间件,桥梁
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
notify() {
this.subs.forEach((sub) => {
sub.update()
})
}
}
class Watcher {
constructor(vm, node, name) {
Dep.target = this //Dep.target 是一个全局变量,存储的是 this指向的 Vue 函数里面的数据
this.vm = vm
this.node = node
this.name = name
this.update()
Dep.target = null //将 Dep.target 变量清空,从而保证Dep.target 里面的数据每次的是最新的
}
update() {
if (this.node.nodeType === 3) {
this.node.nodeValue = this.vm.data[this.name] //去data 里面查找对应的 数据并赋值给对应 元素
} else if (this.node.nodeType === 1) {
this.node.value = this.vm.data[this.name]
}
}
}
浙公网安备 33010602011771号