一个简易的双向绑定demo
2022-04-04 本文已影响0人
苍老师的眼泪
<!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">
<div>
一个叫<strong>{{ name }}</strong>的人,他今年
<strong>{{ age }}</strong>岁,
家住在<strong>{{ home.location }}</strong>
他有一个朋友也是 <strong> {{ age }}</strong> 岁
</div>
</div>
<script>
let data = {
name: "Edison",
age: 26,
home: {
location: 'Guangdong'
}
}
let el = "#app"
let vm = {
}
function reactive(target, obj) {
for (let key of Object.keys(obj)) {
if (typeof obj[key] == 'object') {
vm[key] = {}
reactive(vm[key], obj[key])
return
}
// 记录依赖的watcher
let dep = []
Object.defineProperty(target, key, {
get: function () {
if (global_watcher) {
dep.push(global_watcher)
}
return obj[key]
},
set: function (val) {
// console.log('哈哈, ' + key + '的值为:' + val)
obj[key] = val
dep.forEach(element => {
element.update(val)
});
}
})
}
}
let global_watcher = null
reactive(vm, data)
class Watcher {
constructor(node) {
this.node = node
}
update(new_value) {
this.node.textContent = new_value
}
}
compile(el)
function compile(root) {
let root_node = document.querySelector(root)
compile_node(root_node)
}
function compile_node(node) {
if (node.nodeType == 1) {
// 元素节点
node.childNodes.forEach(child => {
compile_node(child)
})
} else if (node.nodeType == 3) {
// 文本节点
let reg_exp = /\{\{(.*)\}\}/
if (reg_exp.test(node.textContent)) {
let exp = RegExp.$1.trim()
// 怎么才能放到 exp 属性对应的dep里面呢?
// 通过触发getter和setter来访问相应的dep
global_watcher = new Watcher(node.parentNode)
// 触发一下相应的getter, 以便在get中将watcher加入刀相应的dep
// 顺便改一下相应html节点的值
node.textContent = get_value(vm, exp)
global_watcher = null
}
} else
throw '暂时考虑别的类型的节点'
}
function get_value(vm, exp) {
let value = vm
exp.split('.').forEach(e => {
value = value[e]
})
return value
}
</script>
</body>
</html>