-
Notifications
You must be signed in to change notification settings - Fork 14
/
023.Vue双向绑定实现.html
162 lines (147 loc) · 5.91 KB
/
023.Vue双向绑定实现.html
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!DOCTYPE html>
<html lang="en">
<!-- 抄自 技术蛋老师 视频 https://www.bilibili.com/video/BV1934y1a7MN/?spm_id_from=333.788.recommend_more_video.1 -->
<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">
<link rel="stylesheet" href="./assets/global.css">
</head>
<body>
<div id="app">
<span>UP主:{{name}}</span>
<input type="text" v-model="name">
<span>更多:{{more.like}}</span>
<input type="text" v-model="more.like">
</div>
<script>
class Vue {
constructor(obj_instance) {
this.$data = obj_instance.data;
Observer(this.$data);
Complie(obj_instance.el, this)
}
}
// 数据截取 - 监听实例里的数据
function Observer(data_instance) {
// 递归出口
if (!data_instance || typeof data_instance !== "object") return;
const dependency = new Dependency();
Object.keys(data_instance).forEach(key => {
let value = data_instance[key]
Observer(value) // 递归 - 子属性数据劫持
Object.defineProperty(data_instance, key, {
enumerable: true,
configurable: true,
get() {
console.log(`访问了属性:${key} -> 值:${value}`);
// 订阅者加入依赖实例的数组
Dependency.temp && dependency.addSub(Dependency.temp);
return value;
},
set(newValue) {
console.log(`属性${key}的值${value}修改为 -> ${newValue}`);
value = newValue;
Observer(newValue);
dependency.notify()
}
})
})
}
// HTML模板解析 - 替换DOM内的数据
function Complie(element, vm) {
vm.$el = document.querySelector(element);
const fragment = document.createDocumentFragment();
let child;
while (child = vm.$el.firstChild) {
fragment.append(child);
}
fragment_compile(fragment);
// 替换文档碎片内容
function fragment_compile(node) {
const pattern = /\{\{\s*(\S+)\s*\}\}/
if (node.nodeType === 3) {
const xxx = node.nodeValue;
const result_regex = pattern.exec(node.nodeValue);
if (result_regex) {
const arr = result_regex[1].split('.');
const value = arr.reduce(
(total, current) => total[current], vm.$data
)
node.nodeValue = xxx.replace(pattern, value);
// 创建订阅者
new Watcher(vm, result_regex[1], newValue => {
node.nodeValue = xxx.replace(pattern, newValue);
})
console.log(vm.$data[result_regex[1]]);
}
return;
}
if (node.nodeType === 1 && node.nodeName === "INPUT") {
const attr = Array.from(node.attributes);
attr.forEach(i => {
if (i.nodeName === "v-model") {
const value = i.nodeValue.split(".").reduce(
(total, current) => total[current], vm.$data
)
node.value = value;
new Watcher(vm, i.nodeValue, newValue => {
node.value = newValue;
})
node.addEventListener("input", e => {
// ['more','like']
const arr1 = i.nodeValue.split('.')
// ['more']
const arr2 = arr1.slice(0, arr1.length - 1);
// vm.$data.more
const final = arr2.reduce((total, current) => total[current], vm.$data)
// vm.$data.more['like'] = e.target.value
final[arr1[arr1.length - 1]] = e.target.value;
})
}
})
}
node.childNodes.forEach(child => fragment_compile(child))
}
vm.$el.appendChild(fragment);
}
// 依赖 - 收集和通知订阅者
class Dependency {
constructor() {
this.subscribers = []
}
addSub(sub) {
this.subscribers.push(sub)
}
notify() {
this.subscribers.forEach(sub => sub.update())
}
}
// 订阅者
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
// 临时属性 - 触发getter
Dependency.temp = this;
key.split('.').reduce((total, current) => total[current], vm.$data)
Dependency.temp = null;
}
update() {
const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data)
this.callback(value);
}
}
const vm = new Vue({
el: "#app",
data: {
name: "蛋老师",
more: {
like: "一键三连"
}
}
});
</script>
</body>
</html>