React页面切换时避免iframe重新加载的实现方案
问题描述
我正在开发一个使用react-router-dom处理页面的简易React UI,其中包含若干iframe。目前功能正常,但每当用户在包含iframe的页面间来回导航时,iframe都会重新渲染。我需要保留切换页面之前的iframe状态。
复现步骤
- 点击“Page with iframe”
- iframe加载维基百科首页
- 在iframe内滚动或跳转至任意页面
- 点击“Page with content”
- 再次点击“Page with iframe”
当前行为:iframe重新加载,回到维基百科首页
期望行为:iframe显示离开时的状态
示例代码
import React, { useEffect, useState } from "react"; import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom"; function delay(time) { return new Promise((resolve) => setTimeout(resolve, time)); } const fakeApi = async () => { await delay(1000); return { src: "https://fr.wikipedia.org/wiki/Main_Page" }; }; const Page1 = () => <div>Page with sample content</div>; const Page2 = () => { const [iframeUrl, setIframeUrl] = useState(null); const loadIframe = async () => { const { src } = await fakeApi(); setIframeUrl(src); }; useEffect(() => { loadIframe(); }, []); return ( <div> <h3>Page with sample iframe</h3> <p> Eveytime a users moves to this page the iframe is reloaded (ex. if you try to scroll, navigate to "Page with content" and back to "Page with iframe", you'll see a new iframe is loaded) </p> <iframe src={iframeUrl} width="640" height="480" title="test wikipedia" /> </div> ); }; export default () => ( <Router> <ul> <li> <Link to="/contentpage">Page with content</Link> </li> <li> <Link to="/iframepage">Page with iframe</Link> </li> </ul> <hr /> <Routes> <Route exact path="/contentpage" element={<Page1 />} /> <Route path="/iframepage" element={<Page2 />} /> </Routes> </Router> );
解决方案
方法1:将iframe移出路由组件,通过路由状态控制显示/隐藏
核心逻辑是让iframe始终存在于DOM中,仅在切换路由时控制其显示或隐藏,避免组件卸载导致iframe重新加载。
修改后的代码:
import React, { useEffect, useState } from "react"; import { BrowserRouter as Router, Route, Routes, Link, useLocation } from "react-router-dom"; function delay(time) { return new Promise((resolve) => setTimeout(resolve, time)); } const fakeApi = async () => { await delay(1000); return { src: "https://fr.wikipedia.org/wiki/Main_Page" }; }; const Page1 = () => <div>Page with sample content</div>; const Page2 = () => { return ( <div> <h3>Page with sample iframe</h3> <p> Now the iframe preserves its state when navigating back and forth </p> </div> ); }; const IframeContainer = () => { const [iframeUrl, setIframeUrl] = useState(null); const location = useLocation(); useEffect(() => { const loadIframe = async () => { const { src } = await fakeApi(); setIframeUrl(src); }; // 仅在首次加载时获取iframe地址 if (!iframeUrl) { loadIframe(); } }, [iframeUrl]); // 判断当前路由是否为iframe页面,控制显示 const showIframe = location.pathname === "/iframepage"; return ( <div style={{ display: showIframe ? "block" : "none" }}> {iframeUrl && <iframe src={iframeUrl} width="640" height="480" title="test wikipedia" />} </div> ); }; export default () => ( <Router> <ul> <li> <Link to="/contentpage">Page with content</Link> </li> <li> <Link to="/iframepage">Page with iframe</Link> </li> </ul> <hr /> {/* 将iframe容器放在路由外部,避免路由切换时卸载 */} <IframeContainer /> <Routes> <Route exact path="/contentpage" element={<Page1 />} /> <Route path="/iframepage" element={<Page2 />} /> </Routes> </Router> );
方法2:状态提升+React.memo缓存iframe组件
把iframe的状态提升到父组件,并用React.memo包裹iframe组件,确保仅当src属性变化时才重渲染组件,避免路由切换导致的不必要刷新。
示例代码:
import React, { useEffect, useState, memo } from "react"; import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom"; function delay(time) { return new Promise((resolve) => setTimeout(resolve, time)); } const fakeApi = async () => { await delay(1000); return { src: "https://fr.wikipedia.org/wiki/Main_Page" }; }; const Page1 = () => <div>Page with sample content</div>; // 用React.memo缓存iframe组件,仅当src变化时重新渲染 const PersistentIframe = memo(({ src }) => { return <iframe src={src} width="640" height="480" title="test wikipedia" />; }); export default () => { const [iframeUrl, setIframeUrl] = useState(null); useEffect(() => { const loadIframe = async () => { const { src } = await fakeApi(); setIframeUrl(src); }; if (!iframeUrl) { loadIframe(); } }, [iframeUrl]); return ( <Router> <ul> <li> <Link to="/contentpage">Page with content</Link> </li> <li> <Link to="/iframepage">Page with iframe</Link> </li> </ul> <hr /> <Routes> <Route exact path="/contentpage" element={<Page1 />} /> <Route path="/iframepage" element={ <div> <h3>Page with sample iframe</h3> <p> The iframe state is preserved now </p> {iframeUrl && <PersistentIframe src={iframeUrl} />} </div> } /> </Routes> </Router> ); };
方法3:使用keep-alive类第三方库
如果不想手动实现状态管理,可以使用专门的React缓存组件库,这类库能帮你缓存组件实例,避免卸载时丢失状态。使用时只需将需要保留状态的页面组件包裹在对应的缓存组件中即可。
内容的提问来源于stack exchange,提问作者LLL
相关产品推荐
相关产品推荐

