vue 数据双向绑定 个人实现版
程序员文章站
2022-06-27 20:30:10
html
{{text}} ......
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]
}
}
}