尤雨溪说:“管你学不学的动,老子就要更新!” 玩笑开完了,代码撸起来~ :smile:

下面是使用自己实现的双向绑定js在页面的效果:

图片
图片

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 <!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>

<body>
<div id="app"> </div>
<button id="btn">点击</button>
<script src="./vue3.js"></script>
<script>
const root = document.getElementById('app');
const btn = document.getElementById('btn');

// 响应式
const data = {
name: "阿强",
age: 3,
}

// 将数据proxy
const obj = reactive(data);

// computed属性
let age = computed(() => obj.age - 2);

// 数据变化执行的fn
let changed = () => {
// 数据变化 触发set,执行这个函数
console.log('数据变了', obj.age)
// 我们没有compile,不能支持vue的语法
// obj.name 收集依赖
root.innerHTML = `<h1>${obj.name}今年${obj.age}岁了,喜欢隔壁${age.value}岁的小花</h1>`
}

effect(changed);

btn.addEventListener('click', () => {
// 触发set函数,执行effect
obj.age++;
}, false)
</script>
</body>

</html>

vue3.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// key:原始数据 => data:响应式数据
let toProxy = new WeakMap();
// key:响应式数据 => data:原始数据
let toRow = new WeakMap();

// 存储effect的地方 栈
let effectStack = [];

// 存储依赖的地方
let targetMap = new WeakMap();


// 收集依赖
function track(target, key) {
// 栈后进先出,最后一个就是最新的一个数据
const effect = effectStack[effectStack.length - 1];
if (!effect) return;
let depMap = targetMap.get(target);
if (depMap === undefined) {
depMap = new Map();
targetMap.set(target, depMap);
}
let dep = depMap.get(key);
if (dep === undefined) {
dep = new Set();
depMap.set(key, dep);
}

if (!dep.has(effect)) {
dep.add(effect);
effect.deps.push(dep);
}


}

// 触发更新
function trigger(target, key, info) {

// 1.寻找依赖
const depMap = targetMap.get(target);
// 没有依赖
if (depMap === undefined) return;

const effects = new Set(); // 集合,自动去重
const computedRunners = new Set();

if (key) {
let deps = depMap.get(key);
// deps里面全部是effect
deps.forEach(effect => {
if (effect.computed) {
computedRunners.add(effect);
} else {
effects.add(effect);
}
})
}

// 执行更新
effects.forEach(effect => effect());
computedRunners.forEach(computed => computed());

}

// 往effectStack push effect函数,执行fn 数据更新之后需要执行的fn
function effect(fn, options = {}) {
// @todo 处理options
let e = createReactiveEffect(fn, options);

if (!options.lazy) {
e();
}

return e
}

// 构造effect
function createReactiveEffect(fn, options) {

const effect = function effect(...args) {
return run(effect, fn, args);
}
effect.deps = [];
effect.computed = options.computed;
effect.lazy = options.lazy;
return effect;
}

function run(effect, fn, args) {

if (effectStack.indexOf(effect) === -1) {
try {
effectStack.push(effect);
// 执行的时候,是要获取的
return fn(...args);
} finally {
effectStack.pop() // effect用完就要推出去
}
}
}

// 计算属性
function computed(fn) {
const runner = effect(fn, {
computed: true,
lazy: true
});
return {
effect: runner,
get value() {
return runner();
}
}
}


// 响应式
function reactive(target) {

// 查询缓存中数据是否已经被proxy了
let observed = toProxy.get(target);

if (observed) {
return observed;
}

if (toRow.get(target)) {
return target;
}

// 数据没有proxy时,设置缓存
observed = new Proxy(target, baseHandler);
toProxy.set(target, observed);
toRow.set(observed, target);

return observed;

}


// 响应代理
const baseHandler = {
get(target, key) {
// target就是obj,key就是name 以下写法等同于 target[key]
const res = Reflect.get(target, key);
// 收集依赖 track
track(target, key);
return typeof res == 'object' ? reactive(res) : res;
},
set(target, key, val) {
const info = {
oldValue: target[key],
newValue: val
}
// obj.name = xx 这里 我们是需要通知更新的
const res = Reflect.set(target, key, val)
// 触发更新
trigger(target, key, info)
return res
}
}