246 lines
5.1 KiB
Markdown
246 lines
5.1 KiB
Markdown
# 框架相关算法
|
|
|
|
## 1. 虚拟DOM Diff 算法
|
|
|
|
### 简化版 Diff 实现
|
|
|
|
```js
|
|
function diff(oldVNode, newVNode) {
|
|
const patches = [];
|
|
|
|
// 节点不存在
|
|
if (!newVNode) {
|
|
patches.push({ type: 'REMOVE' });
|
|
return patches;
|
|
}
|
|
|
|
// 类型不同,替换节点
|
|
if (typeof oldVNode !== typeof newVNode ||
|
|
(typeof oldVNode === 'string' && oldVNode !== newVNode) ||
|
|
oldVNode.tag !== newVNode.tag) {
|
|
patches.push({ type: 'REPLACE', node: newVNode });
|
|
return patches;
|
|
}
|
|
|
|
// 比较属性
|
|
if (newVNode.props) {
|
|
const propsPatches = diffProps(oldVNode.props || {}, newVNode.props);
|
|
if (Object.keys(propsPatches).length) {
|
|
patches.push({ type: 'PROPS', props: propsPatches });
|
|
}
|
|
}
|
|
|
|
// 比较子节点
|
|
diffChildren(oldVNode.children || [], newVNode.children || [], patches);
|
|
|
|
return patches;
|
|
}
|
|
|
|
function diffProps(oldProps, newProps) {
|
|
const patches = {};
|
|
// 新增或修改的属性
|
|
for (const key in newProps) {
|
|
if (newProps[key] !== oldProps[key]) {
|
|
patches[key] = newProps[key];
|
|
}
|
|
}
|
|
// 删除的属性
|
|
for (const key in oldProps) {
|
|
if (!(key in newProps)) {
|
|
patches[key] = undefined;
|
|
}
|
|
}
|
|
return patches;
|
|
}
|
|
|
|
function diffChildren(oldChildren, newChildren, patches) {
|
|
const len = Math.max(oldChildren.length, newChildren.length);
|
|
for (let i = 0; i < len; i++) {
|
|
const childPatches = diff(oldChildren[i], newChildren[i]);
|
|
if (childPatches.length) {
|
|
patches.push({ type: 'CHILDREN', index: i, patches: childPatches });
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Vue 响应式原理
|
|
|
|
### Vue 2 (Object.defineProperty)
|
|
|
|
```js
|
|
function observe(obj) {
|
|
if (typeof obj !== 'object' || obj === null) return;
|
|
|
|
Object.keys(obj).forEach(key => {
|
|
let value = obj[key];
|
|
const dep = new Set();
|
|
|
|
observe(value); // 递归处理嵌套对象
|
|
|
|
Object.defineProperty(obj, key, {
|
|
get() {
|
|
if (currentEffect) dep.add(currentEffect);
|
|
return value;
|
|
},
|
|
set(newVal) {
|
|
if (newVal === value) return;
|
|
value = newVal;
|
|
observe(newVal);
|
|
dep.forEach(fn => fn());
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
let currentEffect = null;
|
|
function watchEffect(fn) {
|
|
currentEffect = fn;
|
|
fn();
|
|
currentEffect = null;
|
|
}
|
|
```
|
|
|
|
### Vue 3 (Proxy)
|
|
|
|
```js
|
|
function reactive(obj) {
|
|
return new Proxy(obj, {
|
|
get(target, key, receiver) {
|
|
track(target, key);
|
|
const result = Reflect.get(target, key, receiver);
|
|
return typeof result === 'object' ? reactive(result) : result;
|
|
},
|
|
set(target, key, value, receiver) {
|
|
const result = Reflect.set(target, key, value, receiver);
|
|
trigger(target, key);
|
|
return result;
|
|
}
|
|
});
|
|
}
|
|
|
|
const targetMap = new WeakMap();
|
|
let activeEffect = null;
|
|
|
|
function track(target, key) {
|
|
if (!activeEffect) return;
|
|
let depsMap = targetMap.get(target);
|
|
if (!depsMap) targetMap.set(target, (depsMap = new Map()));
|
|
let dep = depsMap.get(key);
|
|
if (!dep) depsMap.set(key, (dep = new Set()));
|
|
dep.add(activeEffect);
|
|
}
|
|
|
|
function trigger(target, key) {
|
|
const depsMap = targetMap.get(target);
|
|
if (!depsMap) return;
|
|
depsMap.get(key)?.forEach(effect => effect());
|
|
}
|
|
|
|
function effect(fn) {
|
|
activeEffect = fn;
|
|
fn();
|
|
activeEffect = null;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. React useState 简单模拟
|
|
|
|
```js
|
|
let state = [];
|
|
let stateIndex = 0;
|
|
|
|
function useState(initialValue) {
|
|
const currentIndex = stateIndex;
|
|
state[currentIndex] = state[currentIndex] ?? initialValue;
|
|
|
|
const setState = (newValue) => {
|
|
state[currentIndex] = typeof newValue === 'function'
|
|
? newValue(state[currentIndex])
|
|
: newValue;
|
|
render(); // 触发重新渲染
|
|
};
|
|
|
|
stateIndex++;
|
|
return [state[currentIndex], setState];
|
|
}
|
|
|
|
function render() {
|
|
stateIndex = 0; // 重置索引
|
|
// 调用组件函数...
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. useEffect 简单模拟
|
|
|
|
```js
|
|
let effectIndex = 0;
|
|
let effects = [];
|
|
|
|
function useEffect(callback, deps) {
|
|
const currentIndex = effectIndex;
|
|
const prevDeps = effects[currentIndex]?.deps;
|
|
|
|
const hasChanged = !prevDeps ||
|
|
deps.some((dep, i) => !Object.is(dep, prevDeps[i]));
|
|
|
|
if (hasChanged) {
|
|
// 执行清理函数
|
|
effects[currentIndex]?.cleanup?.();
|
|
|
|
// 延迟执行 effect
|
|
Promise.resolve().then(() => {
|
|
const cleanup = callback();
|
|
effects[currentIndex] = { deps, cleanup };
|
|
});
|
|
}
|
|
|
|
effectIndex++;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 简易 Redux
|
|
|
|
```js
|
|
function createStore(reducer) {
|
|
let state;
|
|
const listeners = [];
|
|
|
|
const getState = () => state;
|
|
|
|
const dispatch = (action) => {
|
|
state = reducer(state, action);
|
|
listeners.forEach(listener => listener());
|
|
};
|
|
|
|
const subscribe = (listener) => {
|
|
listeners.push(listener);
|
|
return () => {
|
|
const index = listeners.indexOf(listener);
|
|
listeners.splice(index, 1);
|
|
};
|
|
};
|
|
|
|
dispatch({ type: '@@INIT' }); // 初始化 state
|
|
|
|
return { getState, dispatch, subscribe };
|
|
}
|
|
|
|
// 使用示例
|
|
const reducer = (state = { count: 0 }, action) => {
|
|
switch (action.type) {
|
|
case 'INCREMENT': return { count: state.count + 1 };
|
|
case 'DECREMENT': return { count: state.count - 1 };
|
|
default: return state;
|
|
}
|
|
};
|
|
```
|