在 React 中实现代码拆分

2025-06-04

在 React 中实现代码拆分

理解 React 打包

通过使用WebpackBrowserify等工具,React 应用程序可以进行打包。打包意味着应用程序内部的文件会被导入并合并为一个文件。这样,当你在网页中导入应用程序时,只需导入一个文件即可。

假设您有两个文件:

// greeting.js
export const greeting = () => {
  console.log("Hello my friend");
};
// index.js
import { greeting } from "./greeting.js";

greeting();

捆绑包会将这些文件转换为:

const greeting = () => {
  console.log("Hello my friend");
};

greeting();

当然,这过于简单了,因为捆绑过程中有很多步骤,但你明白了。

捆绑问题

当你的应用规模较小时,打包非常有用,但随着应用规模的扩大,打包文件也会随之增大。这意味着,如果用户加载了你的 Web 应用的主页,她仍然需要导入整个应用的打包文件……

这可能会导致性能问题。为了避免这种情况,我们可以实现代码拆分。在本文中,我们将基于路由进行代码拆分。

进口()

代码拆分意味着我们将代码拆分成更小的部分。然而,我们不需要改变代码的编写方式。我们只需要改变导入组件的方式。我们需要告诉负责打包代码的工具何时拆分代码。

如果你使用 create-react-app(它使用了 Webpack),则首先需要使用动态导入功能。语法如下:

import("./myModule.js").then((myModule) => {
  myModule.init();
});

该语法使用承诺来等待 Javascript 文件加载完毕后再使用该文件的内容。

React.lazy

React 使用React.lazy实现了这种逻辑。它允许你像显示其他组件一样显示组件,不同之处在于它将被动态导入。

import React, { lazy } from "react";

const DynamicallyImported = lazy(() => import("./DynamicallyImported.js"));

React.lazy接受一个返回动态导入的函数。该组件不会像未实现任何代码拆分那样与应用程序的其余部分一起导入。React 仅在首次渲染时导入该组件。

注意,你动态导入的组件需要是默认导出的,当然,需要导出一个React组件。

因此,如果我们基于路由实现代码拆分,这意味着如果用户访问了我们的应用程序但从未访问过某个特定路由,那么该路由将不会被导入。这对用户来说是一个很大的好处,因为你只会强制浏览器导入她需要的内容,而不会导入其他内容。

悬念

使用React.lazy导入的组件必须在Suspense组件内部使用。Suspense组件提供了一个 fallback 内容。该内容指示我们的惰性组件正在加载。

import React, { lazy, Suspense } from "react";

const DynamicallyImported = lazy(() => import("./DynamicallyImported.js"));

const CoolComponent = () => {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <DynamicallyImported />
      </Suspense>
    </div>
  );
};

此类动态导入的组件不需要直接被 Suspense 组件包裹。一个 Suspense 组件也可以处理多个动态导入的组件:

import React, { lazy, Suspense } from "react";

const DynamicallyImported = lazy(() => import("./DynamicallyImported.js"));
const AnotherDynamicallyImported = lazy(() =>
  import("./AnotherDynamicallyImported.js")
);

const CoolComponent = () => {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <header>
          <h1>Hello there</h1>
        </header>
        <div>Something else</div>
        <DynamicallyImported />
        <p>Divider</p>
        <AnotherDynamicallyImported />
      </Suspense>
    </div>
  );
};

根据我们的路线进行代码拆分

路由是实现代码拆分的良好起点。从一个页面到另一个页面时,用户通常期望页面以块的形式加载,并等待一段时间才能渲染完成。因此,在确保不影响用户体验的情况下,路由是一个很好的起点。

在这个例子中,我将使用流行的react-router-dom包来管理我的 React 应用程序的路由。当然,你也可以将它与任何你喜欢的库一起使用。

在代码拆分之前,您的路由器组件可能看起来像这样:

import React from "react";
import { Route, Router, Switch } from "react-router-dom";
import Header from "./Header";
import About from "./pages/About";
import Blog from "./pages/Blog";
import Contact from "./pages/Contact";
import Home from "./pages/Home";
import Products from "./pages/Products";
import { createBrowserHistory } from "history";

const history = createBrowserHistory();

export default () => {
  return (
    <Router history={history}>
      <Header />
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/products" component={Products} />
        <Route path="/blog" component={Blog} />
        <Route path="/contact" component={Contact} />
      </Switch>
    </Router>
  );
};

如果你之前使用过react-router-dom,那么应该会很熟悉。如果没有,以下是使用此库实现路由的方法。

有了这段代码,每当用户访问一个页面时,整个应用程序代码都会被加载。因为我们只有一个 bundle,所以没有其他办法!这会很耗资源。

我们需要做三件事来使代码拆分工作并产生多个捆绑包:

  1. 导入React.lazySuspense
  2. 修改我们导入组件(主页、关于、产品、博客和联系人)的方式,使用React.lazyimport()使其动态化
  3. 使用Suspense组件在我们的返回函数中提供后备。

  4. 您可以将第一行更改为:

import React, {lazy, Suspense} from "react";

完毕!

  1. 动态导入我们的组件。将第 4 行至第 8 行修改如下:
const About = lazy(() => import("./pages/About"));
const Blog = lazy(() => import("./pages/Blog"));
const Contact = lazy(() => import("./pages/Contact"));
const Home = lazy(() => import("./pages/Home"));
const Products = lazy(() => import("./pages/Products"));

惊人的!

  1. 最后,用Suspense组件包围动态导入的组件
export default () => {
  return (
    <Router history={history}>
      <Header />
      <Suspense fallback={<div>Loading page...</div>}>
       <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route path="/products" component={Products} />
        <Route path="/blog" component={Blog} />
        <Route path="/contact" component={Contact} />
       </Switch>
      </Suspense>
    </Router>
  );
};

太棒了!你已经成功在 React 应用中实现了代码拆分。用户对应用的全新速度感到满意。是时候庆祝一下了!

我正在使用create-react-app,它使用了 Webpack。如果我npm run build在实现代码拆分后运行,我可以看到 Webpack 正在创建不同的 bundles。

代码拆分包

文件更多,但文件更小。这就是重点 😉

希望以上内容足够清晰。如果还有疑问,欢迎随时在评论区提问。

玩得开心❤️

文章来源:https://dev.to/damcosset/implement-code-splitting-in-react-1c0j
PREV
使用 Cypress.io Cypress 测试库测试 React
NEXT
JavaScript Promises 101