React手把手帶你實(shí)現(xiàn)Keep-Alive效果的三種方法
前言
在 Vue 中,我們可以通過 <keep-alive> 輕松緩存組件實(shí)例。但在 React 中,組件卸載(Unmount)意味著狀態(tài)和 DOM 的徹底銷毀。如何在 React 中實(shí)現(xiàn)“切換頁面不丟失滾動位置、不重置表單”?本文將為你拆解三種主流方案。
一、 Keep-Alive 的本質(zhì)是什么?
在實(shí)現(xiàn) React 版緩存組件前,先明確 Vue keep-alive 的核心邏輯,才能精準(zhǔn)復(fù)刻:
- 核心本質(zhì):緩存組件實(shí)例,保留組件內(nèi)部狀態(tài)(如輸入框內(nèi)容、滾動位置);
- 行為特征:組件切換時(shí)不銷毀 / 重建,僅通過「隱藏 / 顯示」控制渲染狀態(tài);
- React 目標(biāo):實(shí)現(xiàn)和 Vue 一致的效果 —— 組件切走不丟狀態(tài),切回來能恢復(fù)。
二、 React 實(shí)現(xiàn) keep-alive 的 3 種方案
方案 1:CSS 隱藏 + 不卸載組件(最簡單)
核心思路:
通過 display: none 隱藏不活躍的組件,保留組件的 DOM 節(jié)點(diǎn)和內(nèi)部狀態(tài),僅切換 display 屬性控制顯示 / 隱藏,不觸發(fā)組件的卸載 / 重新掛載生命周期。
適用場景
- 少量組件切換(2-3 個,如 tab 標(biāo)簽頁);
- 簡單業(yè)務(wù)場景(如表單頁、列表頁切換)。
優(yōu)缺點(diǎn)
? 優(yōu)點(diǎn):實(shí)現(xiàn)簡單,無額外依賴,狀態(tài)保留完整;
? 缺點(diǎn):所有組件都會掛載在 DOM 樹中,組件數(shù)量多(如 5 個以上)會增加 DOM 節(jié)點(diǎn)數(shù)量,可能影響頁面渲染性能。
import { useState } from 'react';
// 模擬 Tab 切換場景
const KeepAliveByCSS = () => {
// 控制當(dāng)前激活的標(biāo)簽
const [activeKey, setActiveKey] = useState('tab1');
return (
<div>
<div className="tab-header">
<button onClick={() => setActiveKey('tab1')}>標(biāo)簽1</button>
<button onClick={() => setActiveKey('tab2')}>標(biāo)簽2</button>
</div>
<div className="tab-content">
{/* 始終掛載,僅通過 CSS 隱藏 */}
<div style={{ display: activeKey === 'tab1' ? 'block' : 'none' }}>
<Tab1 />
</div>
<div style={{ display: activeKey === 'tab2' ? 'block' : 'none' }}>
<Tab2 />
</div>
</div>
</div>
);
};
// 帶狀態(tài)的子組件
const Tab1 = () => {
// 切換標(biāo)簽后,輸入框內(nèi)容不會丟失
const [inputVal, setInputVal] = useState('');
return <input value={inputVal} onChange={(e) => setInputVal(e.target.value)} placeholder="標(biāo)簽1輸入框" />;
};
const Tab2 = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>計(jì)數(shù):{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
};方案 2:使用react-activation第三方庫(推薦)
這是目前社區(qū)內(nèi)最成熟的方案,它通過將組件渲染到“外部容器”再動態(tài)掛載回來的方式,模擬了 Vue 的行為。
核心功能
- 提供
<KeepAlive>容器組件,包裹需要緩存的組件即可生效; - 內(nèi)置
useActivate/useDeactivate鉤子函數(shù),分別在組件「激活」和「失活」時(shí)觸發(fā); - 支持緩存控制(如指定緩存 Key、條件緩存);
- 能保留 DOM 狀態(tài)(如滾動條位置、輸入框焦點(diǎn))。
使用示例
import { KeepAlive, useActivate, useDeactivate } from 'react-activation';
import { useState } from 'react';
const KeepAliveByLib = () => {
const [show, setShow] = useState(true);
return (
<div>
<button onClick={() => setShow(!show)}>
{show ? '隱藏組件' : '顯示組件'}
</button>
{/* 用 KeepAlive 包裹需要緩存的組件 */}
{show && (
<KeepAlive id="cached-component">
<CachedComponent />
</KeepAlive>
)}
</div>
);
};
// 被緩存的組件
const CachedComponent = () => {
const [inputVal, setInputVal] = useState('');
const [scrollTop, setScrollTop] = useState(0);
// 組件激活時(shí)觸發(fā)(顯示時(shí))
useActivate(() => {
console.log('組件被激活');
});
// 組件失活時(shí)觸發(fā)(隱藏時(shí))
useDeactivate(() => {
console.log('組件被失活');
});
// 模擬滾動條狀態(tài)保留
const handleScroll = (e) => {
setScrollTop(e.target.scrollTop);
};
return (
<div>
<input
value={inputVal}
onChange={(e) => setInputVal(e.target.value)}
placeholder="緩存的輸入框"
/>
<div
style={{ height: '200px', overflow: 'auto', marginTop: '10px' }}
onScroll={handleScroll}
>
{Array.from({ length: 50 }).map((_, index) => (
<p key={index}>滾動測試行 {index + 1}</p>
))}
</div>
<p>當(dāng)前滾動位置:{scrollTop}</p>
</div>
);
};方案 3:全局狀態(tài)管理 + 狀態(tài)回顯(Redux)
如果不想引入第三方庫,也可以通過全局狀態(tài)緩存實(shí)現(xiàn)核心效果,本質(zhì)是組件卸載前存狀態(tài),重新掛載時(shí)取狀態(tài)。
核心思路
在組件 useEffect 的清理函數(shù)中,將關(guān)鍵數(shù)據(jù)(輸入框、計(jì)數(shù)、滾動位置等)保存到全局 Store;重新掛載時(shí)再讀取初始化。
優(yōu)點(diǎn):符合 React 數(shù)據(jù)流規(guī)范,內(nèi)存占用可控。
缺點(diǎn):
- 無法恢復(fù) DOM 狀態(tài):如頁面的滾動位置、輸入框的焦點(diǎn)、已播放的視頻進(jìn)度,需單獨(dú)編寫邏輯處理。
- 開發(fā)成本高:每個需要緩存的組件都要手動編寫保存/恢復(fù)邏輯。
補(bǔ)救措施:若要恢復(fù)滾動位置,需手動在卸載前記錄
scrollTop,并在渲染后通過window.scrollTo還原。
三、緩存 React Router 路由組件
實(shí)際開發(fā)中,最常見的場景是切換路由不丟失頁面狀態(tài)(如列表頁滾動位置、表單輸入內(nèi)容),可結(jié)合 React Router + react-activation 實(shí)現(xiàn)。
核心代碼如下:
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { KeepAlive } from 'react-activation';
import Profile from './pages/Profile';
import Home from './pages/Home';
import Settings from './pages/Settings';
// 路由容器組件(獲取當(dāng)前路由路徑)
const RouterContainer = () => {
const location = useLocation();
const currentPath = location.pathname;
return (
<Routes>
{/* 普通路由(不緩存) */}
<Route path="/" element={<Home />} />
<Route path="/settings" element={<Settings />} />
{/* 緩存路由組件:when 控制是否緩存,id 為緩存標(biāo)識 */}
<Route
path="/profile"
element={
<KeepAlive id="profile" when={currentPath === "/profile"}>
<Profile />
</KeepAlive>
}
/>
</Routes>
);
};
// 根組件
const App = () => {
return (
<BrowserRouter>
<RouterContainer />
</BrowserRouter>
);
};四、 方案選擇建議
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場景 |
|---|---|---|---|
| CSS 隱藏 | 實(shí)現(xiàn)簡單、無依賴 | DOM 節(jié)點(diǎn)冗余、性能一般 | 少量組件(2-3 個)、簡單 tab 切換 |
| react-activation | 功能完整、支持 DOM 狀態(tài)緩存 | 新增第三方依賴 | 中大型項(xiàng)目、需完整 keep-alive 效果 |
| 全局狀態(tài)緩存 | 無額外依賴、貼合狀態(tài)管理 | 僅恢復(fù)數(shù)據(jù)、需手動處理 DOM | 已用全局狀態(tài)庫、僅需數(shù)據(jù)緩存 |
五、總結(jié)
- React 無原生 keep-alive,但可通過CSS 隱藏、react-activation 庫、全局狀態(tài)緩存3 種方案模擬核心效果;
- 簡單場景用 CSS 隱藏,中大型項(xiàng)目優(yōu)先選 react-activation(兼顧易用性和完整性);
- 路由組件緩存可結(jié)合 React Router + react-activation 實(shí)現(xiàn),核心是通過 KeepAlive 包裹路由元素并指定緩存標(biāo)識。
到此這篇關(guān)于React手把手帶你實(shí)現(xiàn)Keep-Alive效果的三種方法的文章就介紹到這了,更多相關(guān)React實(shí)現(xiàn)Keep-Alive內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React Ref Callback使用場景最佳實(shí)踐詳解
這篇文章主要為大家介紹了React Ref Callback使用場景最佳實(shí)踐詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
在React框架中實(shí)現(xiàn)一些AngularJS中ng指令的例子
這篇文章主要介紹了在JavaScript的React框架中實(shí)現(xiàn)一些AngularJS指令的例子,React使用Virtual DOM因而與普通的js框架有些不同,需要的朋友可以參考下2016-03-03
create-react-app項(xiàng)目配置全解析
這篇文章主要為大家介紹了create-react-app項(xiàng)目配置全解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
React Native使用百度Echarts顯示圖表的示例代碼
本篇文章主要介紹了React Native使用百度Echarts顯示圖表的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
React?Native實(shí)現(xiàn)Toast輕提示和loading效果
這篇文章主要介紹了React Native實(shí)現(xiàn)Toast輕提示和loading效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09

