不废话直接上代码,代码可以在本地运行
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" id="a" v-model="text">
{{text}}
</div>
<script src="src/Dep.js"></script>
<script src="src/Observe.js"></script>
<script src="src/Watcher.js"></script>
<script src="src/Compile.js"></script>
<script src="src/MVVM.js"></script>
<script>
var vm = new Vue({
el: 'app',
data: {
text: 'Hello world!'
}
})
</script>
</body>
</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
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
# MVVM.js
//提供了全局的Vue 模版
function Vue(options) {
this.data = options.data //对象 {text:'hello world!'}
var data = this.data //局部变量加快代码速度 减少作用域链的查询
//发布者 例如data 中text发生了变化,发布通知到订阅者
observe(data, this)
var id = options.el;
var dom = new Compile(document.getElementById(id), this) //this Vue的实例 包含着data
//编译完后,将dom返回倒app中
document.getElementById(id).appendChild(dom)
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# Compile.js
function Compile(node, vm) {
if (node) {
this.$frag = this.nodeToFragment(node, vm);
return this.$frag
}
}
Compile.prototype = {
nodeToFragment: function (node, vm) {
var self = this;
var frag = document.createDocumentFragment();
var child;
while (child = node.firstChild) {
self.compileElement(child, vm)
frag.append(child)
}
return frag
},
compileElement: function (node, vm) {
var reg = /\{\{(.*)\}\}/;
//节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes;
for (var i = 0; i < attr.length; i++) {
if (attr[i].nodeName = 'v-model') {
//获取v-model绑定的属性名
var name = attr[i].nodeValue;
//给相应的data属性赋值,进而触发该属性的set方法
node.addEventListener('input', function (e) {
vm[name] = e.target.value;
})
//node.value = vm[name];将data的值赋值给该node
new Watcher(vm, node, name, 'value')
}
}
}
//节点为文本
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
//获取匹配的字符串
var name = RegExp.$1;
name = name.trim();
// node.nodeValue = vm[name];将data的值赋值给node
new Watcher(vm, node, name, 'nodeValue')
}
}
}
}
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
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
# Watcher.js
function Watcher(vm, node, name, type) {
Dep.target = this
this.name = name
this.node = node
this.vm = vm
this.type = type
this.update();
Dep.target = null
}
Watcher.prototype = {
update: function () {
this.get();
//对应的哪行node节点 {{text}}[nodeValue] = (new Vue())['text']
//{{text}} => Hello World
this.node[this.type] = this.value;//订阅者执行相应操作
},
//获取data的属性
get: function () {
this.value = this.vm[this.name]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Observe.js
function defineReactive(obj, key, val) {//obj:vm对象 key:text val:hello world
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function () {
//添加订阅者watcher到主题对象Dep
//target 其实就是watcher对象
if (Dep.target) {
//js浏览器单线程特性,保证在一个时间内只有一个监听器使用
dep.addSub(Dep.target)
}
return val
},
set: function (newVal) {
if (newVal == val) return;
val = newVal;
console.log(val)
//作为发布者发出通知
dep.notify()
}
})
}
//obj: {text: 'Hello world!'}
function observe(obj, vm) {
Object.keys(obj).forEach(key => {
defineReactive(vm, key, obj[key])
})
}
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
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
# Dep.js
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub)
},
notify: function () {
this.subs.forEach(sub => {
sub.update();
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13