Object.defineProperties()模拟实现Vue的绑定原理

咱们先写一个类似的html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>

<body>
<div id="app">
<h5>Vue</h5>
<p>计数:<span>{{count}}</span></p>
<h5>hello word</h5>
<p>计次:<span>{{num}}</span></p>
<p>计数:<span>{{count}}</span></p>
</div>
</body>

</html>

然后实现一个类obsover的函数

一、改造data对象的属性,并保护data中的每个属性
使用Object.keys来遍历data的属性名;Vue的绑定原理,保护data属性,并创建get,set方法这里因为不确定data里面有多少个属性,所以这里使用for of 循环遍历data的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<script>
var data = {
num: 2,
count: 1,
};
var keys = Object.keys(data);
console.log(keys);

for (var key of keys) {
(function (key) {
Object.defineProperties(data, {

//保护当前的变量,定义一个"_key"变量来保护原变量
//在定义对象时,属性名禁止使用模板字符串 所以不能用`_${key}`定义属性名
//在定义对象时,属性名禁止使用拼接字符串 所以不能用 “_”+{key}定义属性名
//ES6语法新语法,专门用于动态生成属性名,这里只能使用[拼接字符串或模板字符串]的方式定义属性名

[`_` + key]: {
value: data[key],
writable: true,
enumerable: false, //半隐藏
},

//用一个和当前属性同名的访问器属性,替代data中的原
普通属性
[key]: {
get() {
return this[`_${key}`];
},
set(value) {
this[`_${key}`] = value;
console.log(`${key}发生了变化`);
change(key);
},
enumerable: true,
},
});
})(key);
}
//密封 每个属性的configurable:false;
Object.seal(data);
console.log(data);
setInterval(function () {
data.count += 1;
}, 1000);
<script>

然后创建虚拟Dom树数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var arr = [];
//定义一个函数,来扫描真实Do树
function getChildren(parent) {
var children = parent.children;
//使用递归遍历出所有Dom树,
for (var c of children) {
if (c.children.length > 0) {
//递归调用,如果还有子节点
arguments.callee(c);
} else {
for (var key of keys) {
//判断内容是不是{{}}的如果是
if (c.innerHTML == `{{${key}}}`) {
//将c和c.innerHTML存入数组中,
arr.push({
elemt: c,
innerHTML: c.innerHTML,
});
//页面初次渲染时,将对应的属性值赋值给{{}}
c.innerHTML = data[key];
}
}
}
}
}
//传入根节点
getChildren(document.getElementById("app"));
console.log(arr);

创造一个watcher

1
2
3
4
5
6
7
8
function change(key) {
//遍历虚拟Dom树,将新值赋值给对应的{{}}
for (var obj of arr) {
if (obj.innerHTML == `{{${key}}}`) {
obj.elemt.innerHTML = data[key];
}
}
}

效果图

Image text

console

Image text