延迟加载 React 组件——并非高深莫测
想象一下,你去一家咖啡馆点了一杯咖啡☕。账单🧾送来时,结账的不仅仅是你点的咖啡,还有咖啡馆里所有菜品的账单。你会是什么感觉?震惊吧!!付清所有你还没点的菜品的钱,这太不公平了。你没有争辩,就付了账,再也没有回到这家咖啡馆。
嗯,这只是个比喻。让我们把它和我们的 Web 平台联系起来,用一个巨大的 JavaScript 包来构建。
在这里,我们的用户是顾客,而我们(开发者)是咖啡馆老板。如果我们的用户只请求了registration/signup
表单,你会把负责其余页面的 JavaScript 包(账单)也发下来吗?这些包里还包含巨大的地图或日期库?你的用户会作何感想?很可能会让用户感到沮丧,或者以后不会再访问你的网站,对吧?
显而易见的原因是,他们的首页加载体验会很慢,并且页面可能需要更多时间才能交互(TTI和FID)。浏览器会忙于解析 JavaScript,而我们的用户只能盯着空白的屏幕,一脸愁容☹️。
可悲的是,我们可怜的用户根本不知道是我们开发人员更有责任心,一次性将完整的 JavaScript 包发送给他们。
欢迎来到代码拆分的世界,在这里您可以动态地延迟加载 JavaScript 包,仅在用户请求时才加载。您交给用户的账单正是他们吃过的😄。
基于路由的拆分
所有现代 JavaScript 打包工具,例如Webpack、Rollup和parcel,都支持开箱即用的代码拆分功能。这些打包工具可以创建多个可在运行时动态加载的 bundle,从而提升用户的 Web 性能。
根据routes/pages
应用中的路由拆分 JavaScript 包称为基于路由的代码拆分。例如,如果您有login
一个主页,您更有可能根据这些路由拆分 JavaScript 包。并且仅login
在页面加载时发送页面 JavaScript。
NextJS开箱即用地提供了这种基于路由的拆分功能。如果你正在使用React Router,那么React-lazy是你的最佳选择。
基于组件的拆分
基于路由的拆分让用户非常满意。现在我们更进一步,实现基于组件的拆分。让我们通过一个例子来理解这一点,然后进行一个编程练习来巩固我们的概念。不知不觉中,它就会变得轻而易举🍰。
假设您正在构建一个页面来展示一处出租房产。页面上有一个按钮,可以打开一个全页地图来显示其地址。这个地图组件功能复杂,并且占用了大量的 JavaScript 代码。
import JSHeavyMapComponent from './js-heavy-map-component';
// Property page component
export default function Property() {
const [showMap, setShowMap] = useState(false);
return <>
<h1>Rental Property</h1>
<article>
<h2>Property description</h2>
{ showMap && <JSHeavyMapComponent /> }
<button onClick={() => setShowMap(true)}>
Show map
</button>
</article>
</>
}
您会将此地图组件作为初始属性页(基于路由)包的一部分吗?如果用户从未点击按钮,而只查看属性元数据怎么办?解析所有额外的 JavaScript 会导致页面加载缓慢,这难道不是浪费资源吗?
是的,在这种情况下,没有必要发送所有这些繁重的 JavaScript 包。这可能会对资源有限的移动用户造成沉重的负担,因为与桌面用户相比。
这时,基于组件的加载就派上用场了,它可以缓解这些问题。通过这种方法,您可以延迟加载地图组件,并在用户实际请求(点击按钮)时动态加载。这将使您的属性页面更加精简,从而提高整体页面加载性能。您可以投入更多精力,在用户即将悬停在按钮上时下载组件,从而节省额外的一微秒时间。
抛开理论不谈,我们将了解如何使用动态导入功能在代码中轻松实现它。我们将看到两个示例,首先从React.lazy方法开始,然后介绍如何在 NextJS 项目中使用动态导入功能实现相同的功能。
那么,让我们开始吧。
通过 React.lazy 实现延迟加载
我们需要使用 React.lazy 和Suspense来动态地延迟加载我们的 Map 组件。
// Change the old import to use React.lazy
const JSHeavyMapComponent = React.lazy(() =>
import("./js-heavy-map-component")
);
// Property page component
export default function Property() {
const [showMap, setShowMap] = useState(false);
return (
<>
<h1>Rental Property</h1>
<article>
<h2>Property description</h2>
{/* Wrap you dynamic component with Suspense */}
{showMap && (
<React.Suspense fallback={<p>Loading...</p>}>
<JSHeavyMapComponent />
</React.Suspense>
)}
<button onClick={() => setShowMap(true)}>Show map</button>
</article>
</>
);
}
因此,通过此更改,当您的属性页加载时,浏览器将不会为地图组件加载额外的 JavaScript。只有当用户点击Show map
按钮时才会加载——只需几行代码,即可节省大量时间。我不是说过这很容易吗?😉?这是codesandbox 的演示network
。下载并在您的计算机上本地运行该应用。点击按钮时,请留意您的标签页Show map
。这是您的lazy-loading
实际操作。
NextJS 中的延迟加载
使用NextJS,实现动态加载就像 ABC 一样简单。与 React.lazy API 类似,NextJS 有一个等效的dynamic模块,它还允许你传递用于加载组件的附加选项。
import dynamic from "next/dynamic";
// Change the old import to use NextJS dynamic import
const JSHeavyMapComponent = dynamic(() => import("./js-heavy-map-component"));
// Property page component
export default function Property() {
const [showMap, setShowMap] = useState(false);
return (
<>
<h1>Rental Property</h1>
<article>
<h2>Property description</h2>
{showMap && <JSHeavyMapComponent />}
<button onClick={() => setShowMap(true)}>Show map</button>
</article>
</>
);
}
如果您想尝试一下,这里有它的codesandbox 演示。
结论
我写这篇文章的主要目的不仅是告诉你如何实现代码拆分(官方文档是这方面的优秀资源),更是为了帮助你思考我们为什么需要它。记住,如果加载时间过长,你漂亮的应用就毫无用处。用户是核心,我们应该谨慎处理发送给客户端的数据。不要让你的用户为额外的 JavaScript 代码买单。有了这么多优秀的工具,我们没有理由不进行代码拆分。你的用户会感谢你的。
思考包容性,思考可及性。
鏂囩珷鏉ユ簮锛�https://dev.to/aman_singh/lazy-loading-react-components-no-rocket-science-ejn