6.1 KiB
6.1 KiB
框架相关算法
1. 虚拟DOM Diff 算法
场景:React/Vue 核心渲染机制、理解为何需要 key 属性、性能优化方向。 解决:直接操作真实 DOM 性能开销大,通过对比新旧虚拟树最小化 DOM 操作。
简化版 Diff 实现
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)
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)
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 为何不能在条件语句中调用。 解决:函数组件无状态,通过闭包和数组模拟类组件的状态能力。
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 等。
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
场景:跨组件状态管理、理解单向数据流架构、实现时间旅行调试。 解决:多组件共享状态混乱,集中管理应用状态并可预测地变更。
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;
}
};