Skip to content

Commit 36caf0a

Browse files
committed
add MVVM simple demo
1 parent 2ded5cd commit 36caf0a

File tree

5 files changed

+211
-0
lines changed

5 files changed

+211
-0
lines changed

vue双向绑定揭秘/MVVM/compile.js

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
function Compile(el, vm) {
2+
this.vm = vm;
3+
this.el = document.querySelector(el) || document.body;
4+
5+
if (this.el) {
6+
this.fragment = this.node2Fragment(this.el); //把节点装进 fragment里面
7+
8+
this.init(); //编译
9+
10+
this.el.appendChild(this.fragment);
11+
}
12+
}
13+
Compile.prototype = {
14+
node2Fragment: function(el) {
15+
var fragment = document.createDocumentFragment();
16+
var child;
17+
while (child = el.firstChild) {
18+
fragment.appendChild(child);
19+
}
20+
return fragment;
21+
},
22+
init: function() {
23+
this.compileCore(this.fragment);
24+
},
25+
compileCore: function(el) {
26+
let childNodes = el.childNodes;
27+
[...childNodes].forEach(node => {
28+
let text = node.textContent;
29+
let reg = /\{\{(.*)\}\}/;
30+
31+
if (this.isElementNode(node)) {
32+
this.compileElement(node);
33+
} else if (this.isTextNode(node) && reg.test(text)) {
34+
this.compileText(node, RegExp.$1);
35+
}
36+
37+
if (node.childNodes && node.childNodes.length) {
38+
this.compileCore(node);
39+
}
40+
});
41+
},
42+
compileElement: function(node) {
43+
let attrs = node.attributes;
44+
[...attrs].forEach(attr => {
45+
let attrName = attr.name; // type v-model
46+
if (this.isDirective(attrName)) {
47+
let value = attr.value;
48+
let name = attrName.substring(2);
49+
if (this.isEventDrective(name)) {
50+
this.eventHandler(node, this.vm, value, name);
51+
} else {
52+
this.bind(node, this.vm, value, 'model');
53+
54+
node.addEventListener('input', (ev) => {
55+
let newVal = ev.target.value;
56+
this._setVMVal(this.vm, value, newVal);
57+
}, false);
58+
}
59+
}
60+
//事件简写 @click="xxx"
61+
if (this.isEventDrective2(attrName)) {
62+
this.eventHandler(node, this.vm, attr.value, attr.name);
63+
}
64+
});
65+
},
66+
compileText: function(node, exp) { //文本 {{text}}
67+
this.bind(node, this.vm, exp, 'text');
68+
},
69+
eventHandler: function(node, vm, fnName, eventName) {
70+
//eventName on:click @click
71+
let eventType = eventName.split(/:|@/)[1];
72+
let fn = vm.methods && vm.methods[fnName];
73+
74+
if (eventType && fn) {
75+
node.addEventListener(eventType, fn.bind(vm), false);
76+
}
77+
},
78+
bind: function(node, vm, exp, sig) {
79+
let updaterFn = this.updater[sig + 'Updater'];
80+
81+
updaterFn && updaterFn(node, this._getVMVal(vm, exp));
82+
83+
new Watcher(vm, exp, (value) => {
84+
updaterFn && updaterFn(node, value);
85+
});
86+
},
87+
_setVMVal: function(vm, exp, newVal) {
88+
vm[exp] = newVal;
89+
},
90+
_getVMVal: function(vm, exp) {
91+
return vm[exp];
92+
},
93+
updater: {
94+
textUpdater: function(node, val) {
95+
node.textContent = val;
96+
},
97+
modelUpdater: function(node, val) {
98+
node.value = val;
99+
}
100+
},
101+
isEventDrective2: function(attr) {
102+
return attr.indexOf('@') == 0;
103+
},
104+
isDirective: function(attr) {
105+
return attr.indexOf('v-') == 0;
106+
},
107+
isEventDrective: function(name) {
108+
return name.indexOf('on') == 0;
109+
},
110+
isElementNode: function(node) {
111+
return node.nodeType == 1;
112+
},
113+
isTextNode: function(node) {
114+
return node.nodeType == 3;
115+
}
116+
};

vue双向绑定揭秘/MVVM/mvvm.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Document</title>
6+
<script src="observer.js"></script>
7+
<script src="watcher.js"></script>
8+
<script src="compile.js"></script>
9+
<script src="mvvm.js"></script>
10+
</head>
11+
<body>
12+
<div id="app">
13+
<input type="text" v-model="text">
14+
<h3 @click="change">{{text}}</h3>
15+
{{text}}
16+
</div>
17+
18+
<script>
19+
let vm=new Vue({
20+
el:'#app',
21+
data:{
22+
text:'welcome vue core'
23+
},
24+
methods:{
25+
change:function(){
26+
this.text='aaa';
27+
}
28+
}
29+
});
30+
</script>
31+
</body>
32+
</html>

vue双向绑定揭秘/MVVM/mvvm.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
function Vue(options) {
2+
this.data = options.data;
3+
this.methods = options.methods;
4+
//observer
5+
this.observer = new Observer(this.data, this);
6+
//compile
7+
this.compile = new Compile(options.el, this);
8+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
function Observer(data, vm) {
2+
this.data = data;
3+
this.vm = vm;
4+
this.init();
5+
}
6+
Observer.prototype = {
7+
init: function() {
8+
Object.keys(this.data).forEach(key => { //数据劫持
9+
this._proxyData(key)
10+
});
11+
},
12+
_proxyData: function(key) {
13+
let dep = new Dep();
14+
15+
let vm = this.vm;
16+
Object.defineProperty(vm, key, {
17+
get: function() {
18+
Dep.target && dep.addSub(Dep.target);
19+
return vm.data[key];
20+
},
21+
set: function(newVal) {
22+
vm.data[key] = newVal;
23+
24+
dep.notify();
25+
}
26+
});
27+
}
28+
};
29+
30+
function Dep() {
31+
this.subs = [];
32+
}
33+
Dep.prototype = {
34+
addSub: function(sub) {
35+
this.subs.push(sub);
36+
},
37+
notify: function() {
38+
this.subs.forEach(sub => {
39+
sub.update();
40+
});
41+
}
42+
};

vue双向绑定揭秘/MVVM/watcher.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function Watcher(vm, exp, fnCb) {
2+
Dep.target = this;
3+
this.vm = vm;
4+
this.exp = exp;
5+
this.cb = fnCb;
6+
this.update(); //初始化,把watcher添加到dep里面,发送通知
7+
Dep.target = null;
8+
}
9+
Watcher.prototype = {
10+
update: function() {
11+
this.cb.call(this.vm, this.vm[this.exp]);
12+
}
13+
};

0 commit comments

Comments
 (0)