1.keepalive原理
基本作用
- 正常情况:组件切换时会被销毁(
beforeUnmount→unmounted),再次进入时重新挂载。 - 使用
<keep-alive>:组件不会销毁,而是被缓存起来(保留状态、DOM 不卸载),下次再切换回来时会从缓存里取出。
核心原理
(1)抽象组件
<keep-alive> 本身不会渲染任何 DOM,它是一个 抽象组件,只在 vnode 层做处理。
(2)缓存机制
内部维护了一个 缓存 Map,key 是组件的唯一标识(通常是 vnode.type + vnode.key),value 是对应的 组件实例 vnode。
const cache = new Map<Key, VNode>();
(3)渲染逻辑
-
当
render时,<keep-alive>会判断子组件是否已经在缓存里:- 存在:直接从缓存里拿到之前的 vnode,并复用组件实例。
- 不存在:创建新组件实例,并放入缓存。
(4)卸载逻辑
- 被缓存的组件不会真的执行
unmounted,而是执行deactivated生命周期钩子。 - 当缓存组件再次激活时,会执行
activated生命周期钩子。
生命周期对比
| 情况 | 普通组件 | keep-alive 缓存组件 |
|---|---|---|
| 第一次挂载 | beforeMount → mounted | beforeMount → mounted |
| 组件切换离开 | beforeUnmount → unmounted | deactivated |
| 组件再次进入 | beforeMount → mounted | activated |
缓存控制
<keep-alive> 支持 include / exclude / max 属性来控制缓存:
- include:只有匹配的组件才会被缓存。
- exclude:匹配的组件不会被缓存。
- max:最多缓存多少个,超过会淘汰最久未使用的(LRU 策略)。
<keep-alive include="A,B" exclude="C" max="10">
<component :is="current"></component>
</keep-alive>
2.react实现keep alive功能
🔹1. 最简单实现:条件渲染 + CSS 隐藏
function App() {
const [tab, setTab] = useState<"A" | "B">("A");
return (
<div>
<button onClick={() => setTab("A")}>A</button>
<button onClick={() => setTab("B")}>B</button>
<div>
<div style={{ display: tab === "A" ? "block" : "none" }}>
<ComponentA />
</div>
<div style={{ display: tab === "B" ? "block" : "none" }}>
<ComponentB />
</div>
</div>
</div>
);
}
🔹2. 封装一个 KeepAlive 组件(带缓存 Map)
import React, { useRef } from "react";
type KeepAliveProps = {
activeKey: string;
children: React.ReactNode[];
};
export default function KeepAlive({ activeKey, children }: KeepAliveProps) {
const cache = useRef(new Map<string, React.ReactNode>());
// 遍历 children,把它们存入缓存
React.Children.forEach(children, (child: any) => {
if (child && child.key) {
cache.current.set(child.key as string, child);
}
});
return (
<>
{Array.from(cache.current.entries()).map(([key, child]) => (
<div key={key} style={{ display: key === activeKey ? "block" : "none" }}>
{child}
</div>
))}
</>
);
}
🔹3. 高阶封装(withKeepAlive)
function withKeepAlive(WrappedComponent: React.ComponentType) {
return React.memo((props) => {
const ref = useRef<JSX.Element>();
if (!ref.current) {
ref.current = <WrappedComponent {...props} />;
}
return ref.current;
});
}
使用方法:
const AliveA = withKeepAlive(ComponentA);
2.watch、watchEffect、computed区别
| 特性 | watch | watchEffect | computed |
|---|---|---|---|
| 执行时机 | 数据变化时 | 立即执行 + 数据变化时 | 依赖变化时(懒计算) |
| 依赖收集 | 手动指定 | 自动收集 | 自动收集 |
| 返回值 | 无 | 无 | 有(返回计算值) |
| 新旧值 | 有 | 无 | 无 |
| 是否缓存 | 否 | 否 | ✅ 有缓存 |
| 适用场景 | 异步/副作用、监听具体数据 | 自动副作用、快速调试 | 计算属性、派生数据 |