261 lines
6.1 KiB
Markdown
261 lines
6.1 KiB
Markdown
# 框架相关算法
|
||
|
||
## 1. 虚拟DOM Diff 算法
|
||
|
||
> **场景**:React/Vue 核心渲染机制、理解为何需要 key 属性、性能优化方向。
|
||
> **解决**:直接操作真实 DOM 性能开销大,通过对比新旧虚拟树最小化 DOM 操作。
|
||
|
||
### 简化版 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 项目开发、理解数据驱动视图更新、排查响应式失效问题。
|
||
> **解决**:手动操作 DOM 繁琐易错,自动追踪数据变化并更新视图。
|
||
|
||
### 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 简单模拟
|
||
|
||
> **场景**:React 函数组件状态管理、理解 Hooks 为何不能在条件语句中调用。
|
||
> **解决**:函数组件无状态,通过闭包和数组模拟类组件的状态能力。
|
||
|
||
```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 简单模拟
|
||
|
||
> **场景**:React 副作用处理(数据请求、事件订阅、定时器)、理解依赖数组作用。
|
||
> **解决**:函数组件中处理生命周期和副作用,替代类组件的 componentDidMount 等。
|
||
|
||
```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;
|
||
}
|
||
};
|
||
```
|