8000 new · devazine/vue-analysis@5e7e020 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5e7e020

Browse files
committed
new
1 parent 16696bc commit 5e7e020

File tree

6 files changed

+336
-2
lines changed

6 files changed

+336
-2
lines changed

2-reactive/1/baseHandlers.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { isObject, isArray, hasOwn, isIntegerKey, hasChanged } from './shared.js'
2+
import { reactive, toRaw, ReactiveFlags, proxyMap } from './reactive.js'
3+
import { track, trigger, ITERATE_KEY, pauseTracking, resetTracking } from './effect.js'
4+
5+
function createArrayInstrumentations() {
6+
const instrumentations = {};
7+
['includes', 'indexOf', 'lastIndexOf'].forEach(key => {
8+
instrumentations[key] = function (...args) {
9+
const arr = toRaw(this);
10+
for (let i = 0, l = this.length; i < l; i++) {
11+
track(arr, "get", i + '');
12+
}
13+
const res = arr[key](...args);
14+
if (res === -1 || res === false) {
15+
return arr[key](...args.map(toRaw));
16+
} else {
17+
return res;
18+
}
19+
};
20+
});
21+
22+
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(key => {
23+
instrumentations[key] = function (...args) {
24+
pauseTracking();
25+
const res = toRaw(this)[key].apply(this, args);
26+
resetTracking();
27+
return res
28+
}
29+
})
30+
return instrumentations
31+
}
32+
33+
const arrayInstrumentations = createArrayInstrumentations();
34+
35+
const get = createGetter();
36+
const set = createSetter();
37+
38+
function createGetter() {
39+
return function get(target, key, receiver) {
40+
if (key === ReactiveFlags.RAW && proxyMap.get(target)) {
41+
return target;
42+
}
43+
44+
const targetIsArray = isArray(target);
45+
46+
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
47+
return Reflect.get(arrayInstrumentations, key, receiver)
48+
}
49+
50+
const res = Reflect.get(target, key, receiver);
51+
track(target, key);
52+
53+
if (isObject(res)) {
54+
return reactive(res);
55+
}
56+
57+
return res;
58+
}
59+
}
60+
61+
function createSetter() {
62+
return function set(target, key, value, receiver) {
63+
let oldValue = target[key]; // 新增
64+
// 新旧值都有可能属于响应式对象
65+
// 将它们都转为原生引用,方便做对比
66+
value = toRaw(value);
67+
oldValue = toRaw(oldValue);
68+
69+
const hadKey = isArray(target) && isIntegerKey(key)
70+
? Number(key) < target.length
71+
: hasOwn(target, key);
72+
73+
const res = Reflect.set(target, key, value, receiver);
74+
if (!hadKey) {
75+
trigger(target, key, 'add', value)
76+
} else if (hasChanged(value, oldValue)) { // 新增判断
77+
trigger(target, key, 'set', value)
78+
}
79+
return res;
80+
}
81+
}
82+
83+
function has(target, key) {
84+
const res = Reflect.has(target, key);
85+
track(target, key);
86+
return res;
87+
}
88+
89+
function ownKeys(target) {
90+
const key = isArray(target) ? 'length' : ITERATE_KEY;
91+
track(target, key);
92+
return Reflect.ownKeys(target);
93+
}
94+
95+
function deleteProperty(target, key) {
96+
const hadKey = hasOwn(target, key);
97+
const res = Reflect.deleteProperty(target, key);
98+
if (res && hadKey) {
99+
trigger(target, key, 'delete');
100+
}
101+
return res
102+
}
103+
104+
105+
export const mutableHandlers = {
106+
get,
107+
set,
108+
deleteProperty,
109+
has,
110+
ownKeys
111+
}

2-reactive/1/effect.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { isArray, isIntegerKey } from './shared.js'
2+
3+
const targetMap = new WeakMap();
4+
export const ITERATE_KEY = Symbol();
5+
6+
export let shouldTrack = true;
7+
const trackStack = [];
8+
9+
// 删除
10+
// let viewEffect;
11+
// export const setViewEffect = (fn) => {
12+
// viewEffect = fn;
13+
// fn();
14+
// }
15+
16+
// 新增
17+
let activeEffect;
18+
19+
// 新增
20+
class ReactiveEffect {
21+
constructor(fn) {
22+
this.fn = fn;
23+
this.deps = [];
24+
}
25+
run() {
26+
activeEffect = this;
27+
shouldTrack = true;
28+
cleanupEffect(this);
29+
return this.fn();
30+
}
31+
}
32+
33+
// 新增
34+
export const effect = (fn) => { // 更名为 effect
35+
const _effect = new ReactiveEffect(fn);
36+
_effect.run();
37+
}
38+
39+
function cleanupEffect(effect) {
40+
const { deps } = effect
41+
if (deps.length) {
42+
for (let i = 0; i < deps.length; i++) {
43+
deps[i].delete(effect)
44+
}
45+
deps.length = 0
46+
}
47+
}
48+
49+
export function pauseTracking() {
50+
trackStack.push(shouldTrack)
51+
shouldTrack = false
52+
}
53+
54+
export function resetTracking() {
55+
const last = trackStack.pop();
56+
shouldTrack = last === undefined ? true : last
57+
}
58+
59+
export const track = (target, key) => {
60+
if (!shouldTrack || !activeEffect) return;
61+
let depsMap = targetMap.get(target)
62+
if (!depsMap) {
63+
targetMap.set(target, (depsMap = new Map()))
64+
}
65+
let dep = depsMap.get(key)
66+
if (!dep) {
67+
depsMap.set(key, (dep = new Set()));
68+
}
69+
70+
// dep.add(viewEffect); // 删除
71+
trackEffects(dep); // 新增
72+
}
73+
74+
// 新增
75+
export function trackEffects(dep){
76+
// 可能
77+
let shouldTrack = !dep.has(activeEffect);
78+
if (shouldTrack) {
79+
dep.add(activeEffect);
80+
activeEffect.deps.push(dep);
81+
}
82+
}
83+
84+
export const trigger = (target, key, type, newValue) => {
85+
const depsMap = targetMap.get(target);
86+
if (!depsMap) {
87+
return
88+
}
89+
90+
let deps = [];
91+
92+
if (key === 'length' && isArray(target)) {
93+
depsMap.forEach((dep, key) => {
94+
if (key === 'length' || key >= newValue) {
95+
deps.push(dep)
96+
}
97+
})
98+
} else {
99+
if (key !== void 0) {
100+
deps.push(depsMap.get(key));
101+
}
102+
103+
switch (type) {
104+
case 'add':
105+
if (!isArray(target)) {
106+
deps.push(depsMap.get(ITERATE_KEY));
107+
} else if (isIntegerKey(key)) {
108+
deps.push(depsMap.get('length'))
109+
}
110+
break;
111+
case 'delete':
112+
if (!isArray(target)) {
113+
deps.push(depsMap.get(ITERATE_KEY));
114+
}
115+
break;
116+
}
117+
}
118+
119+
let viewEffects = [];
120+
for (const dep of deps) {
121+
if (dep) {
122+
viewEffects.push(...dep)
123+
}
124+
}
125+
126+
viewEffects = new Set(viewEffects);
127+
128+
// 新增
129+
triggerEffects(viewEffects);
130+
131+
// 删除
132+
// viewEffects.forEach(effectFn => {
133+
// shouldTrack = true;
134+
// effectFn && effectFn()
135+
// });
136+
}
137+
138+
// 新增
139+
export function triggerEffects(dep){
140+
const depArray = isArray(dep) ? dep : [...dep];
141+
for (const effect of depArray) {
142+
effect.run();
143+
}
144+
}

2-reactive/1/example.html

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<body>
2+
<div></div>
3+
</body>
4+
5+
<script type="module">
6+
import { reactive } from './reactive.js';
7+
import { effect } from './effect.js';
8+
9+
const div = document.querySelector('div');
10+
11+
const arr = reactive(['a', 'b', 'c']);
12+
13+
effect(() => {
14+
arr.push('0');
15+
div.innerText = arr.reduce((prev, cur) => (prev + cur))
16+
});
17+
18+
19+
20+
21+
22+
23+
console.log('----------------');
24+
25+
arr.push('1');
26+
// arr.push('d');
27+
arr[3] = 3
28+
console.log(arr);
29+
</script>

2-reactive/1/reactive.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { mutableHandlers } from './baseHandlers.js'
2+
3+
export const proxyMap = new WeakMap();
4+
export const ReactiveFlags = {
5+
RAW: '__v_raw'
6+
};
7+
8+
export function reactive(target) {
9+
const existingProxy = proxyMap.get(target);
10+
if (existingProxy) {
11+
return existingProxy
12+
}
13+
14+
const proxy = new Proxy(
15+
target,
16+
mutableHandlers
17+
);
18+
19+
proxyMap.set(target, proxy);
20+
return proxy
21+
}
22+
23+
export function toRaw(observed) {
24+
const raw = observed && observed[ReactiveFlags.RAW];
25+
return raw ? toRaw(raw) : observed;
26+
}
27+

2-reactive/1/shared.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export const isObject = val => {
2+
return val !== null && typeof val === 'object'
3+
}
4+
5+
export const isArray = Array.isArray;
6+
7+
const hasOwnProperty = Object.prototype.hasOwnProperty;
8+
export const hasOwn = (target, key) => hasOwnProperty.call(target, key);
9+
10+
export const isString = (val) => typeof val === 'string';
11+
12+
export const isIntegerKey = (key) =>
13+
isString(key) &&
14+
key !== 'NaN' &&
15+
key[0] !== '-' &&
16+
'' + parseInt(key, 10) === key
17+
18+
export const hasChanged = (value, oldValue) =>
19+
!Object.is(value, oldValue)

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ Devazine [《Vue 源码解析》](https://devazine.github.io/#/source-analysis/v
44

55
# 文章 Demo 指南
66

7-
## [reactive 实现](https://devazine.github.io/#/source-analysis/vue/3/)
7+
## [reactive 的实现(上)](https://devazine.github.io/#/source-analysis/vue/3/)
88
- `1-reactive/1` : 二. Track 和 Trigger
99
- `1-reactive/2` : 3.1 嵌套属性处理 - 3.2.2 ownKeys 拦截器
1010
- `1-reactive/3` : 3.3.1 trigger 部分的完善
1111
- `1-reactive/4` : 3.3.2 修复递归追踪问题
1212
- `1-reactive/5` : 3.3.3 toRaw - 3.3.4 检索方法处理
13-
- `1-reactive/6` : 四、代码优化
13+
- `1-reactive/6` : 四、代码优化
14+
15+
## [reactive 的实现(下)](https://devazine.github.io/#/source-analysis/vue/4/)
16+
17+
- `2-reactive/1` : 一. 依赖清理

0 commit comments

Comments
 (0)
0