如何将 React 应用加载时间缩短 70%

2025-05-25

如何将 React 应用加载时间缩短 70%

使用代码分割来减少 React 应用程序初始加载时间的步骤。

我们使用 React 构建大型应用。构建这些应用时,我们面临的主要问题是应用性能。随着应用规模越来越大,性能可能会下降。尤其是应用的初始加载时间会受到更大的影响。应用的初始加载速度必须足够快,不能让用户看到几秒钟的空白屏幕。因为加载时间过长会给用户留下不好的印象。

导致此问题的主要原因是将过多的组件添加到单个 bundle 文件,导致加载该文件可能需要更多时间。为了避免此类问题,我们需要以优化的方式构建组件。为了解决这个问题,React 本身提供了一个原生解决方案,即代码拆分和延迟加载。这可以将 bundle 文件拆分成更小的尺寸。

引入代码拆分的最佳位置是在路由中。基于路由的代码拆分解决了一半的问题。但大多数应用只利用了代码拆分 50% 的优势。

使用代码拆分时,我们是否正确地构建了组件?我们可以通过一些代码示例来了解原因以及如何修复它。为此,我们将使用一个包含一些 UI 组件的 React 应用示例。

在下面的截图中,我们可以看到一个仪表板组件,它有多个选项卡。每个选项卡又有多个组件。

仪表板组件

Dashboard 组件使用基于路由的代码拆分,如下面的代码所示。

const Dashboard = lazy(() => import('components/Dashboard'));
const Settings = lazy(() => import('components/Settings'));
const Configurations = lazy(() => import('components/Configurations'));
function App() {
return (
<Router>
<Layout>
<SideBar/>
<Layout>
<AppHeader/>
<Content style={{padding: '20px'}}>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/dashboard">
<Dashboard/>
</Route>
<Route path="/settings">
<Settings/>
</Route>
<Route path="/configuration">
<Configurations/>
</Route>
</Switch>
</Suspense>
</Content>
</Layout>
</Layout>
</Router>
);
}
const Dashboard = lazy(() => import('components/Dashboard'));
const Settings = lazy(() => import('components/Settings'));
const Configurations = lazy(() => import('components/Configurations'));
function App() {
return (
<Router>
<Layout>
<SideBar/>
<Layout>
<AppHeader/>
<Content style={{padding: '20px'}}>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/dashboard">
<Dashboard/>
</Route>
<Route path="/settings">
<Settings/>
</Route>
<Route path="/configuration">
<Configurations/>
</Route>
</Switch>
</Suspense>
</Content>
</Layout>
</Layout>
</Router>
);
}

仪表板组件包含一些子组件,如销售、利润、图表、图块和趋势,如以下代码所示

function Dashboard() {
return (
<Tabs defaultActiveKey="1">
<TabPane tab="Sales" key="1">
<Sales/>
</TabPane>
<TabPane tab="Profit" key="2">
<Profit/>
</TabPane>
<TabPane tab="Chart" key="3">
<Chart/>
</TabPane>
<TabPane tab="Tiles" key="4">
<Tiles/>
</TabPane>
<TabPane tab="Trends" key="5">
<Trends/>
</TabPane>
</Tabs>
);
}
view raw dashboard.jsx hosted with ❤ by GitHub
function Dashboard() {
return (
<Tabs defaultActiveKey="1">
<TabPane tab="Sales" key="1">
<Sales/>
</TabPane>
<TabPane tab="Profit" key="2">
<Profit/>
</TabPane>
<TabPane tab="Chart" key="3">
<Chart/>
</TabPane>
<TabPane tab="Tiles" key="4">
<Tiles/>
</TabPane>
<TabPane tab="Trends" key="5">
<Trends/>
</TabPane>
</Tabs>
);
}
view raw dashboard.jsx hosted with ❤ by GitHub

我们已将代码拆分成多个路由。因此,当应用程序捆绑时,我们会为每个路由获得一个单独的构建文件,如下所示

构建文件

从上图可以看出,大小为405.1 KB的文件是仪表板组件,其他文件分别是标题、侧边栏、其他组件和 CSS。

我已经将应用程序托管在Netlify中以测试其性能。由于我们在本地测试应用程序,因此无法发现任何差异。当我使用GTmetrix测试托管应用程序时,仪表板屏幕加载耗时2.9 秒,请查看下图逐帧加载的情况。

框架

仪表板组件是此应用程序的初始页面,因此当我们点击应用程序 URL 时,405.1KB文件将与标题和侧边栏一起加载。

最初,用户只会查看“销售”选项卡,但我们的示例应用仪表板组件包含多个选项卡。因此,浏览器还会下载其他选项卡的代码,从而延迟用户的首次绘制。为了减少初始加载时间,我们需要对仪表板组件进行如下更改:

const Sales = lazy(() => import('components/Sales'));
const Profit = lazy(() => import('components/Profit'));
const Chart = lazy(() => import('components/Chart'));
const Tiles = lazy(() => import('components/Tiles'));
const Trends = lazy(() => import('components/Trends'));
const { TabPane } = Tabs;
function Dashboard() {
return (
<Tabs defaultActiveKey="1">
<TabPane tab="Sales" key="1">
<Suspense fallback={<div>Loading...</div>}>
<Sales/>
</Suspense>
</TabPane>
<TabPane tab="Profit" key="2">
<Suspense fallback={<div>Loading...</div>}>
<Profit/>
</Suspense>
</TabPane>
<TabPane tab="Chart" key="3">
<Suspense fallback={<div>Loading...</div>}>
<Chart/>
</Suspense>
</TabPane>
<TabPane tab="Tiles" key="4">
<Suspense fallback={<div>Loading...</div>}>
<Tiles/>
</Suspense>
</TabPane>
<TabPane tab="Trends" key="5">
<Suspense fallback={<div>Loading...</div>}>
<Trends/>
</Suspense>
</TabPane>
</Tabs>
);
}
view raw .jsx hosted with ❤ by GitHub
const Sales = lazy(() => import('components/Sales'));
const Profit = lazy(() => import('components/Profit'));
const Chart = lazy(() => import('components/Chart'));
const Tiles = lazy(() => import('components/Tiles'));
const Trends = lazy(() => import('components/Trends'));
const { TabPane } = Tabs;
function Dashboard() {
return (
<Tabs defaultActiveKey="1">
<TabPane tab="Sales" key="1">
<Suspense fallback={<div>Loading...</div>}>
<Sales/>
</Suspense>
</TabPane>
<TabPane tab="Profit" key="2">
<Suspense fallback={<div>Loading...</div>}>
<Profit/>
</Suspense>
</TabPane>
<TabPane tab="Chart" key="3">
<Suspense fallback={<div>Loading...</div>}>
<Chart/>
</Suspense>
</TabPane>
<TabPane tab="Tiles" key="4">
<Suspense fallback={<div>Loading...</div>}>
<Tiles/>
</Suspense>
</TabPane>
<TabPane tab="Trends" key="5">
<Suspense fallback={<div>Loading...</div>}>
<Trends/>
</Suspense>
</TabPane>
</Tabs>
);
}
view raw .jsx hosted with ❤ by GitHub

在这里,我使用延迟加载导入了每个选项卡组件,并使用悬念包装了组件。

在这里,为了更好地理解,我添加了多个悬念,但您可以对所有组件使用单个悬念。

我没有对路由级别的代码拆分做任何改动。在构建应用时,由于我们对仪表板组件中的每个选项卡进行了延迟加载,因此会添加一些额外的文件。请查看下图了解构建文件拆分的情况。

构建-拆分编码

现在,让我们再次使用 GTmetrix 测试应用,并进行上述更改。查看下图中的应用性能。

框架代码

如您所见,现在我们的仪表板组件仅用1 秒就加载完毕,因为现在只加载了“销售”标签页的代码。通过一些改进,我们缩短了近2 秒。让我们在下图中比较基于路由和基于路由、组件的代码拆分。

基于路由的框架

基于路由的代码拆分

基于组件的框架

基于路由和组件的代码拆分

如您所见,应用程序的初始加载速度有了显著提升。现在,我们通过在仪表盘组件中有效使用代码拆分,进行了一些调整,将 React 应用程序的初始加载时间缩短了 70%。

参考

  1. 代码拆分
  2. 首次内容绘制

结论

以优化的方式构建组件并有效地使用 React API 将提高大型应用程序的性能。

感谢您的阅读。

在Twitter上获取更多更新

你可以给我买杯咖啡来支持我 ☕

电子书

使用 ChatGPT 调试 ReactJS 问题:50 个基本技巧和示例

ReactJS 优化技术和开发资源

更多博客

  1. 使用 Next.js、NextAuth 和 TailwindCSS 的 Twitter 关注者追踪器
  2. 不要优化你的 React 应用,而是使用 Preact
  3. 使用 Next.js、Tailwind 和 Vercel 构建支持暗黑模式的投资组合
  4. React 中不再导入 ../../../
  5. 10 个 React 包,包含 1K 个 UI 组件
  6. Redux Toolkit - 编写 Redux 的标准方法
  7. 5 个软件包可在开发过程中优化和加速您的 React 应用
  8. 如何在 React 中以优化和可扩展的方式使用 Axios
  9. 15 个自定义 Hooks 让你的 React 组件更轻量
  10. 免费托管 React 应用的 10 种方法
  11. 如何在单页应用程序中保护 JWT
文章来源:https://dev.to/nilanth/how-to-reduce-react-app-loading-time-by-70-1kmm
PREV
如何在 React 中以优化和可扩展的方式使用 Axios
NEXT
如何使用 React Router 创建公共和私有路由