Skip to content

Commit e735896

Browse files
author
Xiang Liu
committed
(vue3): watch data and computed
1 parent e6fc724 commit e735896

File tree

3 files changed

+102
-8
lines changed

3 files changed

+102
-8
lines changed

vue/vue3/src/index.js

+36-8
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
* @see https://github.com/zzz945/write-vue3-from-scratch/blob/master/doc/zh-cn.md
55
*/
66
import {VNode} from "./vnode";
7+
import {Watcher} from "./watcher";
78

89
export class Vue3 {
910
constructor(options) {
1011
this.$options = options;
1112
this.initProps();
1213
this.proxy = this.initDataProxy();
14+
this.initWatcher();
1315
this.initWatch();
1416

1517
return this.proxy;
@@ -19,7 +21,7 @@ export class Vue3 {
1921
* @see https://stackoverflow.com/questions/37714787/can-i-extend-proxy-with-an-es2015-class
2022
*/
2123
initDataProxy() {
22-
const data = this.$options.data ? this.$options.data(): {};
24+
const data = this.$data = this.$options.data ? this.$options.data(): {};
2325
const props = this.$props;
2426
const computed = this.$options.computed || {};
2527

@@ -112,11 +114,34 @@ export class Vue3 {
112114
this.$watch(key, this.update.bind(this)); // 依赖收集
113115
this.collected[key] = true;
114116
}
117+
118+
if (this.$target) {
119+
this.$watch(key, this.$target.update.bind(this.$target));
120+
}
115121
}
116122

117-
initWatch() {
123+
initWatcher() {
118124
this.dataNotifyChain = {};
119125
}
126+
127+
initWatch() {
128+
const watch = this.$options.watch || {};
129+
const data = this.$data;
130+
const computed = this.$options.computed || {};
131+
132+
for (let key in watch) {
133+
const handler = watch[key];
134+
135+
if (key in data) {
136+
this.$watch(key, handler.bind(this.proxy));
137+
} else if (key in computed) {
138+
new Watcher(this.proxy, computed[key], handler);
139+
} else {
140+
throw 'the watching key must be keys of data or computed';
141+
}
142+
}
143+
144+
}
120145

121146
notify(key, prev, current) {
122147
(this.dataNotifyChain[key] || []).forEach((cb) => cb(prev, current));
@@ -202,13 +227,16 @@ export class Vue3 {
202227
}
203228

204229
update() {
205-
const parent = this.$el.parentElement;
206-
const vnode = this.$options.render.call(this.proxy, this.createElement.bind(this));
207-
const oldElement = this.$el;
208-
this.$el = this.patch(null, vnode);
230+
const parent = (this.$el || {}).parentElement;
209231

210-
if (parent) {
211-
parent.replaceChild(this.$el, oldElement);
232+
if (this.$options.render) {
233+
const vnode = this.$options.render.call(this.proxy, this.createElement.bind(this));
234+
const oldElement = this.$el;
235+
this.$el = this.patch(null, vnode);
236+
237+
if (parent) {
238+
parent.replaceChild(this.$el, oldElement);
239+
}
212240
}
213241
}
214242

vue/vue3/src/watcher.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
export class Watcher {
3+
constructor(vm, computedHandler, watchHandler) {
4+
this.vm = vm;
5+
this.computedHandler = computedHandler;
6+
this.watchHandler = watchHandler;
7+
8+
vm.$target = this;
9+
this.value = this.computedHandler.call(vm);
10+
vm.$target = null;
11+
}
12+
13+
update() {
14+
const old = this.value;
15+
this.value = this.computedHandler.call(this.vm);
16+
this.watchHandler.call(this.vm, old, this.value);
17+
}
18+
}

vue/vue3/test/watch.spec.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {Vue3} from "../src";
2+
3+
describe('watch', () => {
4+
let cb;
5+
6+
beforeEach(() => {
7+
cb = jasmine.createSpy('cb');
8+
});
9+
10+
it('watch data', () => {
11+
const vm = new Vue3({
12+
data() {
13+
return {a: 0};
14+
},
15+
watch: {
16+
a(pre, current) {
17+
cb(pre, current);
18+
}
19+
}
20+
});
21+
22+
vm.a = 1;
23+
expect(cb).toHaveBeenCalledWith(0, 1);
24+
});
25+
26+
it('watch computed', () => {
27+
const vm = new Vue3({
28+
data() {
29+
return {a: 0};
30+
},
31+
computed: {
32+
b() {
33+
return this.a + 1;
34+
},
35+
},
36+
watch: {
37+
b(pre, current) {
38+
cb(pre, current);
39+
}
40+
}
41+
});
42+
43+
expect(vm.b).toEqual(1);
44+
vm.a = 1;
45+
expect(vm.b).toEqual(2);
46+
expect(cb).toHaveBeenCalledWith(1, 2);
47+
});
48+
});

0 commit comments

Comments
 (0)