别再让用户等哭!React 懒加载:让组件 “躺平” 到要用时再干活
一、React 里的 “组件懒加载”React.lazy 动态导入我们先创建一个Demo组件并看这行代码// 别直接import Demo改成动态导入 const Demo lazy(() import(./Demo));这行代码的魔力在于原本import Demo from ./Demo会在页面初始化时就加载 Demo 组件的代码用React.lazyimport()包裹后Demo 的代码会被单独打包成一个小文件只有当 Demo 组件要被渲染比如滚到可视区域时这个小文件才会被下载、加载。搭配Suspense不然组件加载时会 “崩”不过光用lazy还不够 —— 组件加载需要时间加载过程中页面会报错。得用Suspense当 “占位符”import { lazy, Suspense } from react; const Demo lazy(() import(./Demo)); export default function App() { return ( div {/* 一堆内容... */} Suspense fallback{div组件加载中.../div} Demo / /Suspense /div ); }fallback里写的就是组件加载时的“Loading 占位”比如转圈圈、提示文字用户体验会更友好。二、React 里的 “图片 / 内容懒加载”react-lazyload或 自定义组件组件懒加载管的是“代码包”图片 / 内容懒加载管的是“可视区域内才渲染内容”。我常用的有两种方案方案 1直接用react-lazyload库现成的轮子导入的LazyLoad就是这个库直接包在图片外面import LazyLoad from react-lazyload; // 在App.jsx里用 LazyLoad placeholder{div图片加载中.../div} offset{300} img srchttps://xxx.png alt / /LazyLoadplaceholder图片加载前的占位内容offset{300}提前300px开始加载图片用户还没滚到图片已经在偷偷加载了体验更丝滑。完整代码为了更好展现效果我放了50个p标签import LazyLoad from react-lazyload; import { lazy } from react // import Demo from ./Demo import MyLazyLoad from ./MyLazyLoad // 组件没有出现在可视区域时组件代码都不会被加载被import(./Demo)包裹的模块会单独打包 const Demo lazy(() import(./Demo)); export default function App() { return ( div {/* Demo/Demo */} pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p pxxx/p MyLazyLoad placeholder{divloading.../div} width100px onContentVisible{() console.log(onContentVisible)} onClose{() console.log(onClose)} {/* img srchttps://inews.gtimg.com/om_bt/OG4Cnt2SgXAuTj-Vv77ASGszUj1BwOhUXtBCplSlBfQmAAA/641 alt / */} Demo/Demo /MyLazyLoad LazyLoad placeholder{divloading.../div} offset{300} img srchttps://inews.gtimg.com/om_bt/Os3eJ8u3SgB3Kd-zrRRhgfR5hUvdwcVPKUTNO6O7sZfUwAA/641 alt / /LazyLoad /div ) }可以看到最开始的界面显示的都是loading我们慢慢往下滑滑到Demo和照片方案 2自己写一个懒加载组件比如我自己写的MyLazyLoad.jsx我写的MyLazyLoad组件核心就是用IntersectionObserver实现“可视区域检测”逻辑和之前普通网页的懒加载是通的// MyLazyLoad.jsx核心代码 import { useState, useRef, useEffect } from react; export default function MyLazyLoad(props) { const { placeholder, children, offset, onContentVisible } props; const [visible, setVisible] useState(false); const containerRef useRef(null); const elementObserver useRef(); // 初始化IntersectionObserver useEffect(() { const options { threshold: 0, rootMargin: typeof offset number ? ${offset}px : 0px }; // 监听元素是否进入可视区域 elementObserver.current new IntersectionObserver(lazyLoadHandler, options); const node containerRef.current; elementObserver.current.observe(node); // 卸载时停止监听 return () { elementObserver.current.unobserve(node); }; }, []); // 元素进入可视区域后的处理 function lazyLoadHandler(entries) { const [entry] entries; if (entry.isIntersecting) { setVisible(true); // 显示真实内容 onContentVisible?.(); // 触发“内容可见”的回调 elementObserver.current.unobserve(containerRef.current); // 停止监听 } } // 没进入可视区域就显示占位进入了就显示真实内容 return ( div ref{containerRef} {visible ? children : placeholder} /div ); }用的时候和react-lazyload差不多把要懒加载的内容包进去MyLazyLoad placeholder{div加载中.../div} offset{300} img srchttps://xxx.png alt / {/* 甚至可以包组件Demo / */} /MyLazyLoad三、“组件懒加载”“内容懒加载”双剑合璧把前面的技术结合起来就是React 项目里的“终极懒加载”import { lazy, Suspense } from react; import MyLazyLoad from ./MyLazyLoad; // 组件代码懒加载 const Demo lazy(() import(./Demo)); export default function App() { return ( div {/* 一堆内容... */} {/* 组件代码组件内容 都懒加载 */} Suspense fallback{div组件包加载中.../div} MyLazyLoad placeholder{div组件内容加载中.../div} Demo / /MyLazyLoad /Suspense /div ); }第一步用户滑到Demo 区域前Demo的代码包不会下载第二步代码包下载完成后MyLazyLoad会等 Demo进入可视区域再渲染 Demo 的内容全程都有占位提示用户不会看到 “空白” 或 “报错”。四、总结React 懒加载分两类React.lazySuspense实现组件代码按需下载react-lazyload/ 自定义组件实现内容按需渲染。核心价值减少初始化资源开销提升页面加载和渲染性能。结语普通网页的懒加载是让图片“摸鱼”React 的懒加载则是给组件和代码都发了“摸鱼许可证”—— 不用一开场就全员到岗该躺平时躺平该干活时再发力。就好比经营一家店不用把所有商品都堆在门口初始化加载所有代码而是把暂时没人要的商品放进仓库单独打包组件等顾客问到了再取出来摆上货架渲染内容—— 既省了门口的空间页面加载性能又不会让顾客等太久优化用户体验。