Skip to content

Commit 90193b7

Browse files
committed
kvue
1 parent cb62dec commit 90193b7

File tree

4 files changed

+327
-0
lines changed

4 files changed

+327
-0
lines changed

kvue/01-reactive.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// 给一个obj定义一个响应式的属性
2+
function defineReactive(obj, key, val) {
3+
// 递归
4+
// val如果是个对象,就需要递归处理
5+
observe(val)
6+
7+
Object.defineProperty(obj, key, {
8+
get() {
9+
console.log("get", key);
10+
return val;
11+
},
12+
set(newVal) {
13+
if (newVal !== val) {
14+
console.log("set", key);
15+
val = newVal;
16+
// 新值如果是对象,仍然需要递归遍历处理
17+
observe(newVal)
18+
// update()
19+
}
20+
},
21+
});
22+
}
23+
24+
// 遍历响应式处理
25+
function observe(obj) {
26+
if (typeof obj !== "object" || obj == null) {
27+
return obj;
28+
}
29+
30+
Object.keys(obj).forEach((key) => defineReactive(obj, key, obj[key]));
31+
}
32+
33+
function set(obj, key, val) {
34+
defineReactive(obj, key, val)
35+
}
36+
37+
const obj = {
38+
foo: 'foo',
39+
bar: 'bar',
40+
baz: {
41+
n: 1
42+
}
43+
};
44+
// defineReactive(obj, "foo", "foo");
45+
observe(obj)
46+
// obj.foo;
47+
// obj.baz = {
48+
// n: 10
49+
// }
50+
// obj.baz.n
51+
// obj.dong = 'dong'
52+
set(obj, 'dong', 'dong')
53+
obj.dong

kvue/02-reactive.html

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<div id="app"></div>
2+
3+
<script>
4+
// 给一个obj定义一个响应式的属性
5+
function defineReactive(obj, key, val) {
6+
Object.defineProperty(obj, key, {
7+
get() {
8+
console.log('get', key);
9+
return val
10+
},
11+
set(newVal) {
12+
if (newVal !== val) {
13+
console.log('set', key);
14+
val = newVal
15+
// 视图更新
16+
update()
17+
}
18+
},
19+
})
20+
}
21+
22+
const obj = {}
23+
defineReactive(obj, 'foo', 'foo')
24+
25+
function update() {
26+
app.innerText = obj.foo
27+
}
28+
29+
setInterval(() => {
30+
obj.foo = new Date().toLocaleTimeString()
31+
}, 1000);
32+
</script>

kvue/kvue.html

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
2+
<div id="app">
3+
<p @click="add">{{counter}}</p>
4+
<p>{{counter}}</p>
5+
<p k-text="counter"></p>
6+
<p k-html="desc"></p>
7+
<input type="text" k-model="desc">
8+
</div>
9+
10+
<script src="./kvue.js"></script>
11+
<script>
12+
const app = new KVue({
13+
el: '#app',
14+
data: {
15+
counter: 1,
16+
desc: '<span style="color:red">村长真棒</span>'
17+
},
18+
methods: {
19+
add() {
20+
this.counter++
21+
}
22+
},
23+
})
24+
setInterval(() => {
25+
app.counter++
26+
}, 1000);
27+
</script>

kvue/kvue.js

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// 给一个obj定义一个响应式的属性
2+
function defineReactive(obj, key, val) {
3+
// 递归
4+
// val如果是个对象,就需要递归处理
5+
observe(val);
6+
7+
// 创建Dep实例
8+
const dep = new Dep()
9+
10+
Object.defineProperty(obj, key, {
11+
get() {
12+
console.log("get", key);
13+
// 依赖关系收集
14+
Dep.target && dep.addDep(Dep.target)
15+
return val;
16+
},
17+
set(newVal) {
18+
if (newVal !== val) {
19+
console.log("set", key);
20+
val = newVal;
21+
// 新值如果是对象,仍然需要递归遍历处理
22+
observe(newVal);
23+
// update()
24+
dep.notify()
25+
}
26+
},
27+
});
28+
}
29+
30+
// 遍历响应式处理
31+
function observe(obj) {
32+
if (typeof obj !== "object" || obj == null) {
33+
return obj;
34+
}
35+
36+
new Observer(obj);
37+
}
38+
39+
// 能够将传入对象中的所有key代理到指定对象上
40+
function proxy(vm) {
41+
Object.keys(vm.$data).forEach((key) => {
42+
Object.defineProperty(vm, key, {
43+
get() {
44+
return vm.$data[key];
45+
},
46+
set(v) {
47+
vm.$data[key] = v;
48+
},
49+
});
50+
});
51+
}
52+
53+
class Observer {
54+
constructor(obj) {
55+
// 判断传入obj类型,做相应处理
56+
if (Array.isArray(obj)) {
57+
// todo
58+
} else {
59+
this.walk(obj);
60+
}
61+
}
62+
63+
walk(obj) {
64+
Object.keys(obj).forEach((key) => defineReactive(obj, key, obj[key]));
65+
}
66+
}
67+
68+
class KVue {
69+
constructor(options) {
70+
// 0.保存选项
71+
this.$options = options;
72+
this.$data = options.data;
73+
74+
// 1.对data做响应式处理
75+
observe(options.data);
76+
77+
// 2.代理
78+
proxy(this);
79+
80+
// 3.编译
81+
new Compile(options.el, this);
82+
}
83+
}
84+
85+
class Compile {
86+
constructor(el, vm) {
87+
this.$vm = vm;
88+
this.$el = document.querySelector(el);
89+
90+
if (this.$el) {
91+
this.compile(this.$el);
92+
}
93+
}
94+
95+
// 遍历node,判断节点类型,做不同处理
96+
compile(node) {
97+
const childNodes = node.childNodes;
98+
99+
Array.from(childNodes).forEach((n) => {
100+
// 判断类型
101+
if (this.isElement(n)) {
102+
// console.log('编译元素', n.nodeName);
103+
this.compileElement(n);
104+
// 递归
105+
if (n.childNodes.length > 0) {
106+
this.compile(n);
107+
}
108+
} else if (this.isInter(n)) {
109+
// 动态插值表达式
110+
// console.log('编译文本', n.textContent);
111+
this.compileText(n);
112+
}
113+
});
114+
}
115+
116+
isElement(n) {
117+
return n.nodeType === 1;
118+
}
119+
120+
// 形如{{ooxx}}
121+
isInter(n) {
122+
return n.nodeType === 3 && /\{\{(.*)\}\}/.test(n.textContent);
123+
}
124+
125+
// 编译插值文本 {{ooxx}}
126+
compileText(n) {
127+
// 获取表达式
128+
// n.textContent = this.$vm[RegExp.$1];
129+
this.update(n, RegExp.$1, "text");
130+
}
131+
132+
// 编译元素:遍历它的所有特性,看是否k-开头指令,或者@事件
133+
compileElement(n) {
134+
const attrs = n.attributes;
135+
Array.from(attrs).forEach((attr) => {
136+
// k-text="xxx"
137+
// name = k-text,value = xxx
138+
const attrName = attr.name;
139+
const exp = attr.value;
140+
// 指令
141+
if (this.isDir(attrName)) {
142+
// 执行特定指令处理函数
143+
const dir = attrName.substring(2);
144+
this[dir] && this[dir](n, exp);
145+
}
146+
});
147+
}
148+
149+
update(node, exp, dir) {
150+
// 1.init
151+
const fn = this[dir + 'Updater']
152+
fn && fn(node, this.$vm[exp])
153+
154+
// 2.update
155+
new Watcher(this.$vm, exp, val => {
156+
fn && fn(node, val)
157+
})
158+
}
159+
160+
// k-text
161+
text(node, exp) {
162+
this.update(node, exp, "text");
163+
}
164+
165+
textUpdater(node, val) {
166+
node.textContent = val;
167+
}
168+
169+
// k-html
170+
html(node, exp) {
171+
this.update(node, exp, "html");
172+
}
173+
174+
htmlUpdater(node, val) {
175+
node.innerHTML = val;
176+
}
177+
178+
isDir(attrName) {
179+
return attrName.startsWith("k-");
180+
}
181+
}
182+
183+
// 负责dom更新
184+
class Watcher {
185+
constructor(vm, key, updater) {
186+
this.vm = vm;
187+
this.key = key;
188+
this.updater = updater;
189+
190+
// 触发一下get
191+
Dep.target = this
192+
this.vm[this.key]
193+
Dep.target = null
194+
}
195+
196+
// 将来会被Dep调用
197+
update() {
198+
this.updater.call(this.vm, this.vm[this.key]);
199+
}
200+
}
201+
202+
// 保存watcher实例的依赖类
203+
class Dep {
204+
constructor() {
205+
this.deps = []
206+
}
207+
// 此处dep就是Watcher的实例
208+
addDep(dep) {
209+
// 创建依赖关系时调用
210+
this.deps.push(dep)
211+
}
212+
notify() {
213+
this.deps.forEach(dep => dep.update())
214+
}
215+
}

0 commit comments

Comments
 (0)