-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path03formal-demo.html
180 lines (159 loc) · 4.09 KB
/
03formal-demo.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>正式双向绑定</title>
</head>
<body>
<div id="app">
<input type="text" v-model="text" id="a" />
{{ text }}
</div>
<script type="text/javascript">
/* example#1
var app = document.getElementById('app');
var dom = nodeToFragment(app);
console.log(dom);
function nodeToFragment(node) {
var flag = document.createDocumentFragment();
var child;
while(child = node.firstChild) {
flag.appendChild(child);//劫持node的所有子节点
}
return flag;
}
app.appendChild(dom);//返回到app中
*/
/* example#2
//编译模板数据
function compile(node ,vm) {
var reg = /\{\{(.*)\}\}/;
//根据节点类型来渲染
if(node.nodeType === 1) { //元素
var attrs = node.attributes;
for(var i = 0, j = attrs.length; i < j; i ++) {
var attr = attrs[i];
if(attr.nodeName === 'v-model') {
var name = attr.nodeValue;
node.value = vm.data[name];
node.removeAttribute('v-model');
}
}
} else if(node.nodeType === 3) { //text
if(reg.test(node.nodeValue)) {
var name = RegExp.$1.trim();
node.nodeValue = vm.data[name];
}
}
}
*/
/*
* 初始化绑定数据思路
* 1.劫持目标元素的内部代码段
* 2.每个代码段进行编译,绑定初始化数据
* 监听数据变化思路
* 1.每个绑定属性的文本dom都是一个订阅者
* 2.每个属性中会有一个主体对象,主体对象会存储所有绑定该属性的订阅者对象
* 3.在文本框输入内容触发set访问器时,给订阅者发布消息
*/
//劫持代码段
function nodeToFragment(node, vm) {
var dom = document.createDocumentFragment();
var child = null;
while(child = node.firstChild) {
//每个节点都需要经过编译
compile(child, vm);
dom.appendChild(child);
}
return dom;
}
//编译代码段中每个节点
function compile(node, vm) {
var reg = /\{\{(.*)\}\}/;
if(node.nodeType === 1) { //元素
var attrs = node.attributes;
for( var i = 0; i < attrs.length; i++) {
var attr = attrs[i];
if(attr.nodeName === 'v-model') {
var name = attr.nodeValue;
node.addEventListener('input', function(e) {
e = e || window.event;
vm.data[name] = e.target.value;
});
node.value = vm.data[name];
node.removeAttribute('v-model');
}
}
} else if(node.nodeType === 3) { //文本
if(reg.test(node.nodeValue)) {
var name = RegExp.$1.trim();
new Watcher(vm.data, name, node);
}
}
}
//定义观察者对象
function Watcher(vm, name, node) {
Dep.target = this;
this.vm = vm;
this.name = name;
this.node = node;
this.update();
Dep.target = null;
}
Watcher.prototype.update = function() {
this.value = this.vm[this.name];//触发响应属性的get访问器
this.node.nodeValue = this.value;
}
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
}
//利用访问器来监听对象上数据的变化
function defineReactive(obj, key, val) {
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function() {
if(Dep.target) dep.addSub(Dep.target);
return val;
},
set: function(newVal) {
if(val === newVal) return false;
val = newVal;
//发布者发出消息
dep.notify();
}
});
}
//循环监听对象上所有的键
function observe(vm) {
Object.keys(vm).forEach(function(key) {
defineReactive(vm, key, vm[key]);
});
}
//Vue构造函数
function Vue(options) {
this.data = options.data;
observe(this.data);
var app = document.getElementById(options.el);
var dom = nodeToFragment(app, this);
app.appendChild(dom);
}
//创建Vue实例
var vm = new Vue({
el: 'app',
data: {
text: 'hello VUE'
}
});
</script>
</body>
</html>