Skip to content

Commit 7432fa1

Browse files
author
wangyang
committed
first commit
0 parents  commit 7432fa1

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

Mvue.js

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
const compileUtils = {
2+
3+
getContentValue(value, vm) {
4+
return value.replace(/\{\{(.+?)\}\}/g, (...args) => {
5+
return this.getValue(args[1], vm);
6+
})
7+
},
8+
9+
text(value, node, vm) {
10+
let val;
11+
if (value.indexOf("{{") != -1) { //存在{{}}
12+
//这里的正则很关键
13+
val = value.replace(/\{\{(.+?)\}\}/g, (...args) => {
14+
new Watcher(vm, args[1], (newVal) => {
15+
this.update.textUpdate(node, this.getContentValue(value, vm))
16+
})
17+
return this.getValue(args[1], vm);
18+
})
19+
} else {
20+
val = this.getValue(value, vm);
21+
}
22+
this.update.textUpdate(node, val)
23+
},
24+
html(value, node, vm) {
25+
const val = this.getValue(value, vm);
26+
console.log("new watcher html")
27+
new Watcher(vm, value, (newVal) => {
28+
this.update.htmlUpdate(node, newVal)
29+
})
30+
31+
this.update.htmlUpdate(node, val)
32+
},
33+
model(value, node, vm) {
34+
const val = this.getValue(value, vm);
35+
new Watcher(vm, value, (newVal) => {
36+
this.update.modelUpdate(node, newVal)
37+
})
38+
39+
//双向绑定,监听input事件
40+
node.addEventListener('input', (e) => {
41+
this.setVal(value, vm, e.target.value)
42+
})
43+
44+
this.update.modelUpdate(node, val)
45+
},
46+
//value自定义的事件名字
47+
//event:原生事件名字
48+
on(value, node, vm, event) {
49+
let fn = vm.$option.methods && vm.$option.methods[value];
50+
node.addEventListener(event, fn.bind(vm), false)
51+
52+
},
53+
54+
//双向绑定
55+
setVal(value, vm, inputVal) {
56+
value.split(".").reduce((data, currentVal) => {
57+
data[currentVal] = inputVal
58+
}, vm.$data)
59+
},
60+
61+
getValue(value, vm) {
62+
//reduce是高阶函数,下面内容主要是解决person.age.a.b的情况
63+
//data[person][age][a][b]
64+
return value.split(".").reduce((data, currentVal) => {
65+
return data[currentVal];
66+
}, vm.$data)
67+
},
68+
69+
update: {
70+
modelUpdate(node, value) {
71+
node.value = value
72+
},
73+
htmlUpdate(node, value) {
74+
node.innerHTML = value
75+
},
76+
textUpdate(node, value) {
77+
node.textContent = value;
78+
}
79+
}
80+
}
81+
82+
class Compile {
83+
constructor(el, vm) {
84+
this.el = this.isElementNode(el) ? el : document.querySelector(el);
85+
this.vm = vm;
86+
87+
//1.获取文档碎片对象,放入内存中减少页面的回流和重汇
88+
89+
let fragment = this.node2Fragemts(this.el);
90+
91+
//2.编译模版
92+
this.compile(fragment)
93+
94+
//3.追加子元素到跟元素
95+
this.el.appendChild(fragment);
96+
}
97+
98+
compile(fragment) {
99+
const nodes = fragment.childNodes;
100+
[...nodes].forEach(child => {
101+
if (this.isElementNode(child)) {
102+
//元素节点
103+
// console.log(child)
104+
this.compileElement(child)
105+
} else {
106+
//文本节点
107+
// console.log(child)
108+
this.compileText(child)
109+
}
110+
111+
if (child.childNodes && child.childNodes.length) {
112+
this.compile(child)
113+
}
114+
})
115+
}
116+
117+
//编译节点元素
118+
compileElement(node) {
119+
const attributes = node.attributes;
120+
[...attributes].forEach(attr => {
121+
const {
122+
name,
123+
value
124+
} = attr; //name:v-text,value:'msg'
125+
if (this.isDirective(name)) {
126+
const [, directive] = name.split("-"); //分割属性 text,bind,html,on:click
127+
const [dirName, event] = directive.split(":"); //分割事件text,bind,click
128+
compileUtils[dirName](value, node, this.vm, event);
129+
130+
//删除v-开头的属性
131+
node.removeAttribute("v-" + directive)
132+
} else if (this.isEventName(name)) {
133+
const [, event] = name.split("@");
134+
compileUtils['on'](value, node, this.vm, event);
135+
}
136+
137+
})
138+
}
139+
140+
isEventName(name) {
141+
return name.startsWith("@")
142+
}
143+
144+
//判断是否以v-开头
145+
isDirective(name) {
146+
return name.startsWith("v-")
147+
}
148+
149+
//编译文本元素
150+
compileText(node) {
151+
const content = node.textContent; //{{msg}}
152+
let result = /\{\{(.+?)\}\}/.test(content);
153+
if (result) {
154+
compileUtils['text'](content, node, this.vm)
155+
}
156+
}
157+
158+
//将所有的子元素添加到文档碎片上
159+
node2Fragemts(node) {
160+
const f = document.createDocumentFragment()
161+
let child;
162+
//如果child被append后,node.firstChild会指向下一步子节点
163+
while (child = node.firstChild) {
164+
f.appendChild(child)
165+
}
166+
return f
167+
}
168+
169+
//判断是否是节点元素
170+
isElementNode(node) {
171+
return node.nodeType === 1;
172+
}
173+
}
174+
175+
176+
class Mvue {
177+
//每一个构造函数都有一个prototype属性,指向另一个对象
178+
//这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
179+
constructor(options) {
180+
this.$el = options.el;
181+
this.$data = options.data;
182+
this.$option = options;
183+
184+
if (this.$el) {
185+
//1.实现一个观察者
186+
new Observer(this.$data);
187+
188+
//2.实现一个指令解析器
189+
new Compile(this.$el, this)
190+
191+
this.proxy(this.$data)
192+
193+
new Proxy(this, this.handler)
194+
}
195+
}
196+
197+
let handler = {
198+
get (target, key, receiver) {
199+
target[key]
200+
return Reflect.get(target, key, receiver)
201+
},
202+
set (target, key, value, receiver) {
203+
console.log('set', key, value)
204+
return Reflect.set(target, key, value, receiver)
205+
}
206+
}
207+
208+
209+
proxy(data) {
210+
for (const key in data) {
211+
Object.defineProperty(this, key, {
212+
get() {
213+
return data[key];
214+
},
215+
set(newVal) {
216+
data[key] = newVal;
217+
}
218+
})
219+
}
220+
}
221+
}

Observer.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
class Watcher {
2+
constructor(vm, expr, cb) {
3+
this.vm = vm;
4+
this.expr = expr;
5+
this.cb = cb;
6+
7+
//保存旧值
8+
this.oldVal = this.getOldVal();
9+
}
10+
11+
getOldVal() {
12+
Dep.target = this;
13+
console.log("getOldVal before")
14+
const oldVal = compileUtils.getValue(this.expr, this.vm);
15+
console.log("getOldVal after")
16+
Dep.target = null;
17+
return oldVal;
18+
}
19+
20+
notify() {
21+
const newVal = compileUtils.getValue(this.expr, this.vm);
22+
if (this.oldVal != newVal) {
23+
this.cb(newVal)
24+
}
25+
}
26+
}
27+
28+
29+
class Dep {
30+
constructor() {
31+
this.subs = [];
32+
}
33+
34+
addSub(watcher) {
35+
this.subs.push(watcher);
36+
}
37+
38+
notify() {
39+
this.subs.forEach(w => w.notify())
40+
}
41+
}
42+
43+
class Observer {
44+
constructor(data) {
45+
// this.dep = new Dep()
46+
this.observer(data)
47+
}
48+
49+
observer(data) {
50+
if (data && typeof data === 'object') {
51+
Object.keys(data).forEach(key => {
52+
this.defineReactive(data, key, data[key])
53+
})
54+
}
55+
}
56+
57+
defineReactive(obj, key, value) {
58+
//递归遍历
59+
this.observer(value);
60+
let dep=new Dep()
61+
console.log("defineReactive"+key)
62+
Object.defineProperty(obj, key, {
63+
enumerable: true,
64+
configurable: false,
65+
set: (newVal) => {
66+
console.log("set"+newVal)
67+
//监听新的对象修改
68+
this.observer(newVal)
69+
//监听值的修改
70+
if (newVal != value) {
71+
value = newVal
72+
}
73+
dep.notify()
74+
},
75+
get() {
76+
// console.log("get"+key)
77+
console.log(Dep.target)
78+
Dep.target && dep.addSub(Dep.target)
79+
//订阅数据变化,王dep中添加观察者,dep是观察者集合
80+
return value;
81+
}
82+
})
83+
}
84+
85+
}

index.html

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title></title>
7+
</head>
8+
<body>
9+
10+
<div id="app">
11+
<h2>{{person.name}} --- {{person.age}}</h2>
12+
<h3>{{person.fav}}</h3>
13+
<ul>
14+
<li>1</li>
15+
<li>2</li>
16+
<li>3</li>
17+
</ul>
18+
<h3>{{msg}}</h3>
19+
<div v-text='msg'></div>
20+
<div v-html='htmlStr'></div>
21+
<input type="text" v-model="msg" />
22+
<div>
23+
<button v-on:click="btn">
24+
on点击
25+
</button>
26+
<button @click="btn">
27+
@点击
28+
</button>
29+
</div>
30+
31+
</div>
32+
33+
</body>
34+
<script src="./Mvue.js"></script>
35+
<script src="Observer.js" type="text/javascript" charset="utf-8"></script>
36+
<script>
37+
let vm = new Mvue({
38+
el:'#app',
39+
data:{
40+
person:{
41+
name:'小马哥',
42+
age:'21',
43+
fav:'姑娘'
44+
},
45+
msg:'学习MVVM原理',
46+
htmlStr:"<b>bold</b>"
47+
},
48+
methods:{
49+
btn(){
50+
console.log(this)
51+
}
52+
}
53+
})
54+
55+
</script>
56+
</html>

0 commit comments

Comments
 (0)