Skip to content

Commit fb03443

Browse files
committed
new
1 parent 5e7e020 commit fb03443

17 files changed

+1216
-10
lines changed

2-reactive/1/example.html

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,7 @@
1515
div.innerText = arr.reduce((prev, cur) => (prev + cur))
1616
});
1717

18-
19-
20-
21-
22-
23-
console.log('----------------');
24-
2518
arr.push('1');
26-
// arr.push('d');
2719
arr[3] = 3
28-
console.log(arr);
20+
2921
</script>

2-reactive/2/baseHandlers.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { isObject, isArray, hasOwn, isIntegerKey, hasChanged, isSymbol } 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+
// 新增
39+
const builtInSymbols = new Set(
40+
Object.getOwnPropertyNames(Symbol)
41+
.map(key => (Symbol)[key])
42+
.filter(isSymbol)
43+
)
44+
45+
function createGetter() {
46+
return function get(target, key, receiver) {
47+
if (key === ReactiveFlags.RAW && proxyMap.get(target)) {
48+
return target;
49+
}
50+
51+
const targetIsArray = isArray(target);
52+
53+
if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
54+
return Reflect.get(arrayInstrumentations, key, receiver)
55+
}
56+
57+
const res = Reflect.get(target, key, receiver);
58+
59+
// 新增,判断是否原生内置的 symbol
60+
if (isSymbol(key) && builtInSymbols.has(key)) {
61+
return res; // 绕过 track
62+
}
63+
64+
track(target, key);
65+
66+
if (isObject(res)) {
67+
return reactive(res);
68+
}
69+
70+
return res;
71+
}
72+
}
73+
74+
function createSetter() {
75+
return function set(target, key, value, receiver) {
76+
let oldValue = target[key];
77+
value = toRaw(value);
78+
oldValue = toRaw(oldValue);
79+
80+
const hadKey = isArray(target) && isIntegerKey(key)
81+
? Number(key) < target.length
82+
: hasOwn(target, key);
83+
84+
const res = Reflect.set(target, key, value, receiver);
85+
if (!hadKey) {
86+
trigger(target, key, 'add', value)
87+
} else if (hasChanged(value, oldValue)) {
88+
trigger(target, key, 'set', value)
89+
}
90+
return res;
91+
}
92+
}
93+
94+
function has(target, key) {
95+
const res = Reflect.has(target, key);
96+
track(target, key);
97+
return res;
98+
}
99+
100+
function ownKeys(target) {
101+
const key = isArray(target) ? 'length' : ITERATE_KEY;
102+
track(target, key);
103+
return Reflect.ownKeys(target);
104+
}
105+
106+
function deleteProperty(target, key) {
107+
const hadKey = hasOwn(target, key);
108+
const res = Reflect.deleteProperty(target, key);
109+
if (res && hadKey) {
110+
trigger(target, key, 'delete');
111+
}
112+
return res
113+
}
114+
115+
116+
export const mutableHandlers = {
117+
get,
118+
set,
119+
deleteProperty,
120+
has,
121+
ownKeys
122+
}

2-reactive/2/effect.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
let activeEffect;
10+
11+
class ReactiveEffect {
12+
constructor(fn) {
13+
this.fn = fn;
14+
this.deps = [];
15+
this.parent = undefined; // 新增
16+
}
17+
run() {
18+
// 新增
19+
let parent = activeEffect;
20+
let lastShouldTrack = shouldTrack;
21+
while (parent) {
22+
// 如果自己内部嵌套了自己,退出 run 执行
23+
if (parent === this) {
24+
return;
25+
}
26+
parent = parent.parent;
27+
}
28+
29+
try {
30+
this.parent = activeEffect; // 加上父级信息
31+
activeEffect = this;
32+
shouldTrack = true;
33+
cleanupEffect(this);
34+
return this.fn();
35+
} finally { // 子级执行完,会回到父级继续执行,需要恢复父级信息
36+
activeEffect = this.parent; // 执行完重置 activeEffect 为父级实例
37+
shouldTrack = lastShouldTrack; // 执行完恢复父级的 shouldTrack 状态
38+
this.parent = undefined; // 执行完重置父级属性,避免污染未来的再次调用
39+
}
40+
41+
// 删除
42+
// activeEffect = this;
43+
// shouldTrack = true;
44+
// cleanupEffect(this);
45+
// return this.fn();
46+
}
47+
}
48+
49+
export const effect = (fn) => {
50+
const _effect = new ReactiveEffect(fn);
51+
_effect.run();
52+
}
53+
54+
function cleanupEffect(effect) {
55+
const { deps } = effect
56+
if (deps.length) {
57+
for (let i = 0; i < deps.length; i++) {
58+
deps[i].delete(effect)
59+
}
60+
deps.length = 0
61+
}
62+
}
63+
64+
export function pauseTracking() {
65+
trackStack.push(shouldTrack)
66+
shouldTrack = false
67+
}
68+
69+
export function resetTracking() {
70+
const last = trackStack.pop();
71+
shouldTrack = last === undefined ? true : last
72+
}
73+
74+
export const track = (target, key) => {
75+
if (!shouldTrack || !activeEffect) return;
76+
let depsMap = targetMap.get(target)
77+
if (!depsMap) {
78+
targetMap.set(target, (depsMap = new Map()))
79+
}
80+
let dep = depsMap.get(key)
81+
if (!dep) {
82+
depsMap.set(key, (dep = new Set()));
83+
}
84+
85+
trackEffects(dep);
86+
}
87+
88+
export function trackEffects(dep) {
89+
let shouldTrack = !dep.has(activeEffect);
90+
if (shouldTrack) {
91+
dep.add(activeEffect);
92+
activeEffect.deps.push(dep);
93+
}
94+
}
95+
96+
export const trigger = (target, key, type, newValue) => {
97+
const depsMap = targetMap.get(target);
98+
if (!depsMap) {
99+
return
100+
}
101+
102+
let deps = [];
103+
104+
if (key === 'length' && isArray(target)) {
105+
depsMap.forEach((dep, key) => {
106+
if (key === 'length' || key >= newValue) {
107+
deps.push(dep)
108+
}
109+
})
110+
} else {
111+
if (key !== void 0) {
112+
deps.push(depsMap.get(key));
113+
}
114+
115+
switch (type) {
116+
case 'add':
117+
if (!isArray(target)) {
118+
deps.push(depsMap.get(ITERATE_KEY));
119+
} else if (isIntegerKey(key)) {
120+
deps.push(depsMap.get('length'))
121+
}
122+
break;
123+
case 'delete':
124+
if (!isArray(target)) {
125+
deps.push(depsMap.get(ITERATE_KEY));
126+
}
127+
break;
128+
}
129+
}
130+
131+
let viewEffects = [];
132+
for (const dep of deps) {
133+
if (dep) {
134+
viewEffects.push(...dep)
135+
}
136+
}
137+
138+
viewEffects = new Set(viewEffects);
139+
140+
triggerEffects(viewEffects);
141+
}
142+
143+
export function triggerEffects(dep) {
144+
const depArray = isArray(dep) ? dep : [...dep];
145+
for (const effect of depArray) {
146+
effect.run();
147+
}
148+
}

2-reactive/2/example.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<body>
2+
<div id="effect1"></div>
3+
<div id="effect2"></div>
4+
</body>
5+
6+
<script type="module">
7+
import { reactive } from './reactive.js';
8+
import { effect } from './effect.js';
9+
10+
const div1 = document.querySelector('#effect1');
11+
const div2 = document.querySelector('#effect2');
12+
13+
const data = reactive({
14+
msg1: 'msg1',
15+
msg2: 'msg2'
16+
})
17+
18+
effect(function effectFn1() {
19+
console.log('running effect1...')
20+
effect(function effectFn2() {
21+
console.log('running effect2...')
22+
div2.innerText = data.msg2;
23+
});
24+
25+
div1.innerText = data.msg1;
26+
});
27+
28+
data.msg1 = 'msg1 changes..';
29+
30+
</script>

2-reactive/2/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/2/shared.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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)
20+
21+
export const isSymbol = (val) => typeof val === 'symbol'

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy