Posted by Socrates on | Featured
庞大的JavaScript代码包会降低应用程序的运行速度。当大量代码同时被加载时,用户需要等待更长时间才能看到页面内容,而且页面的反应速度也会变慢。搜索引擎也可能会将这类网站的排名降得较低。
懒加载技术通过将代码分割成较小的部分,并仅在真正需要时才进行加载,从而帮助解决这一问题。
本指南将为您详细介绍如何在React和Next.js中使用懒加载功能。阅读完本指南后,您将了解何时该使用`React.lazy`、`next/dynamic`以及`Suspense`,同时还会获得一些可供您复制并应用于自己项目的实际示例。
目录
什么是懒加载?
懒加载是一种性能优化技术,它会在代码真正被需要时才进行加载。与其一次性加载整个应用程序,不如将其分割成较小的部分——只有当用户访问某个特定页面或使用某项功能时,浏览器才会下载相应的代码片段。
懒加载带来的好处包括:
-
更快的初始加载速度:由于代码被分成了小块,因此应用程序的启动速度会更快。
-
更好的核心Web性能指标:懒加载有助于提升“最大内容绘制时间”和“总阻塞时间”等关键指标。
-
更低的带宽消耗:用户只需下载实际需要的代码,从而节省了网络资源。
在React中,您可以通过动态导入功能以及`React.lazy()`来实现懒加载;而在Next.js中,则可以使用`next/dynamic`来完成同样的任务。
先决条件
在开始学习之前,请确保您已经具备以下条件:
-
对React有基本的了解,包括组件、钩子以及状态管理等概念。
-
已安装Node.js(建议使用18版或更高版本)。
-
拥有一个React应用程序(可以使用Create React App或Vite创建),或者一个Next.js应用程序(用于后续的示例学习)。
对于React相关的示例,您可以选择使用Create React App或Vite;而对于Next.js的示例,则需要使用App Router功能(Next.js 13版及以上版本支持)。
如何使用`React.lazy`进行代码分割
React.lazy()允许你将某个组件定义为动态导入的组件。React只会在该组件首次被渲染时才会加载它。React.lazy() 需要一个能够返回动态调用的 import() 函数。被导入的模块必须使用默认导出方式。
下面是一个基本的示例:
import { lazy } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const AdminDashboard = lazy(() => import('./Admin Dashboard');
function App() {
return (
);
}
如果使用带名称的导出,你可以将它们映射为默认导出:
const ComponentWithNamedExport = lazy(() => {
import('./MyComponent').then((module) => ({
default: module NamedComponent,
}));
});
你还可以为这些代码块指定名称,以便在浏览器中进行更便捷的调试:
const HeavyChart = lazy(() => {
import(/* webpackChunkName: "heavy-chart" */ './HeavyChart');
});
单凭 React.lazy() 是不够的。你必须将这些懒加载组件包裹在 Suspense 中,这样 React 才知道在它们加载期间应该显示什么内容。
如何将 Suspense 与 React.lazy 一起使用
Suspense 是一个 React 组件,当它的子组件正在加载时,它会显示替代内容。它与 React.lazy() 结合使用,可以处理动态导入组件的加载状态。
将你的懒加载组件包裹在 Suspense 中,并提供一个 fallback 属性:
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const AdminDashboard = lazy(() => import('./Admin Dashboard');
function App() {
return (
我的应用
正在加载图表...
正在加载管理面板...
);
}
你也可以为多个懒加载组件使用同一个 Suspense 结构:
正在加载中...
一个设计得更精良的替代内容显示方式,能够提升用户体验:
function LoadingSpinner() {
return (
);
}
}>
如何使用错误边界来处理错误
React.lazy() 和 Suspense 并不能处理加载错误(例如网络故障或某些代码块缺失)。为此,你需要使用错误边界。
“错误边界”是一种组件,它使用`componentDidCatch`或`static getDerivedStateFromError`来捕获其子树中出现的错误,并显示替代界面。
下面是一个简单的错误边界示例:
import { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('错误被错误边界捕获:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || 发生了错误。
;
}
return this.props.children;
}
}
请将你的`Suspense`组件包裹在错误边界中:
import { lazy, Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary';
const HeavyChart = lazy(() => import('./HeavyChart');
function App() {
return (
图表加载失败,请重试。
正在加载图表……
);
}
如果某个组件无法成功加载,错误边界会捕获这个错误,并显示替代界面,而不会让页面显示空白内容或出现未处理的错误。
如何在Next.js中使用`next/dynamic`
Next.js提供了`next/dynamic`这个功能,它结合了`React.lazy()`和`Suspense`,并为Next.js添加了一些专门的设计选项(包括服务器端渲染功能)。
基本用法如下:
'use client';
import dynamic from 'next/dynamic';
const ComponentA = dynamic(() => import('../components/A'));
const ComponentB = dynamic(() => import('../components/B');
export default function Page() {
return (
);
}
自定义加载界面
你可以通过设置`loading`选项,在组件加载期间显示占位符:
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
loading: () => 正在加载图表……
,
});
禁用服务器端渲染
对于那些只能在客户端运行的组件(例如那些使用`window`或仅浏览器可用API的组件),请将`ssr: false`设置为这些组件的属性:
const ClientOnlyMap = dynamic(() => import('../components/Map'), {
ssr: false,
loading: () => 正在加载地图……
,
});
注意:`ssr: false`仅适用于客户端组件。请在包含`’use client’`语句的文件中使用这个选项。
按需加载组件
只有当满足特定条件时,你才能加载某个组件:
'use client';
import { useState } from 'react';
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('../components/Modal'), {
loading: () => 正在加载模态框...
,
});
export default function Page() {
const [showModal, setShowModal] = useState(false);
return (
{showModal && }
);
}
命名导出
对于需要命名导出的情况,你需要通过动态导入来获取相应的组件:
const Hello = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
);
结合 next/dynamic 使用 Suspense
在 React 18 及更高版本中,你可以使用 suspense: true,这样就可以依赖父组件的 Suspense 结构来处理加载逻辑,而无需使用 loading 选项:
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
suspense: true,
});
// 在你的组件中:
正在加载中...