一、vue.js
1 import reactive from "./reactive";
2 import Watcher from "./Watcher";
3 import computed from "./computed";
4 import watch from "./watch";
5
6 export { reactive, Watcher, computed, watch };
二、reactive.js
1 import { isObject } from "./utils";
2 import Dep from "./Dep";
3
4 export default function reactive(data) {
5 if (isObject(data)) {
6 Object.keys(data).forEach(function (key) {
7 defineReactive(data, key);
8 });
9 }
10 return data;
11 }
12 function defineReactive(data, key) {
13 let val = data[key];
14 const dep = new Dep();
15
16 Object.defineProperty(data, key, {
17 get() {
18 dep.depend();
19 return val;
20 },
21 set(newVal) {
22 val = newVal;
23 dep.notify();
24 },
25 });
26
27 if (isObject(val)) {
28 reactive(val);
29 }
30 }
三、utils.js
1 export function isObject(val) {
2 return typeof val == "object";
3 }
四、Dep.js
1 export default class Dep {
2 constructor() {
3 this.deps = new Set();
4 }
5 depend() {
6 if (Dep.target) {
7 this.deps.add(Dep.target);
8 }
9 }
10 notify() {
11 this.deps.forEach(function (watcher) {
12 watcher.update();
13 });
14 }
15 }
16 Dep.target = null;
17
18 const targetStack = [];
19
20 export function pushTarget(_target) {
21 if (Dep.target) {
22 targetStack.push(Dep.target);
23 }
24 Dep.target = _target;
25 }
26
27 export function popTarget() {
28 if (targetStack.length) {
29 Dep.target = targetStack.pop();
30 } else {
31 Dep.target = null;
32 }
33 }
五、Watcher.js
1 import Dep, { pushTarget, popTarget } from "./Dep";
2
3 export default class Watcher {
4 constructor(getter, options = {}) {
5 const { computed, watch, callback } = options;
6
7 this.getter = getter;
8 this.computed = computed;
9 this.watch = watch;
10 this.callback = callback;
11 this.value = "";
12
13 if (computed) {
14 this.dep = new Dep();
15 } else {
16 this.get();
17 }
18 }
19 depend() {
20 this.dep.depend();
21 }
22 get() {
23 pushTarget(this);
24 const value = this.getter();
25 popTarget();
26 if (this.watch) {
27 this.value = value;
28 }
29 return value;
30 }
31 update() {
32 if (this.computed) {
33 this.get();
34 this.dep.notify();
35 } else if (this.watch) {
36 const oldVal = this.value;
37 this.get();
38 this.callback(this.value, oldVal);
39 } else {
40 this.get();
41 }
42 }
43 }
六、computed.js
1 import Watcher from "./Watcher";
2
3 export default function computed(getter) {
4 let def = {};
5 const watcher = new Watcher(getter, { computed: true });
6 Object.defineProperty(def, "value", {
7 get() {
8 watcher.depend();
9 return watcher.get();
10 },
11 });
12 return def;
13 }
七、watch.js
1 import Watcher from "./Watcher";
2
3 export default function watch(getter, callback) {
4 new Watcher(getter, { watch: true, callback });
5 }
八、index.html
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
8 <title>Document</title>
9 </head>
10 <body>
11 <div id="app">
12 <div class="watcher">
13 watcher view:
14 <span class="text"></span>
15 <button class="change">change</button>
16 </div>
17 <div class="computed">
18 computed view:
19 <span class="text"></span>
20 <button class="change">change</button>
21 </div>
22 </div>
23 <script type="module" src="./main.js"></script>
24 </body>
25 </html>
九、server.js
1 const http = require("http"),
2 fs = require("fs");
3
4 const server = new http.Server();
5
6 server.on("request", (req, res) => {
7 const url = req.url;
8 const suffix = url.match(/(?<=\.)\w*$/)?.[0] || "js"; // 匹配文件格式
9 const fileName = url.match(/.*(?=\.\w*$)|\/?\w*/)[0]; // 匹配文件名
10 let mime = "";
11 switch (suffix) {
12 case "html":
13 mime = "text/html";
14 break;
15 case "ico":
16 mime = "image/x-icon";
17 break;
18 default:
19 mime = "text/javascript";
20 }
21
22 fs.readFile(`.${fileName}.${suffix}`, (err, data) => {
23 res.writeHead(200, {
24 "Content-Type": mime,
25 });
26 res.write(data);
27 res.end();
28 });
29 });
30
31 server.listen(80);
十、启动
node server.js ,浏览器访问:http://localhost/index.html