React 代码分割实战:用 React.lazy 与 Vite manualChunks 把首屏包拆薄

test12026-09-304 次阅读

为什么需要代码分割

单页应用打包后往往是一个巨大的 JS 文件,用户首屏就要下载全部代码,即使很多路由暂时用不到。代码分割(code splitting)把不同路由、不同第三方库拆成独立的 chunk,浏览器按需加载,首屏体积和解析时间都能明显下降。

用 React.lazy 与 Suspense 懒加载路由

React 内置了 React.lazy,配合 Suspense 可以把路由组件改成按需加载。组件被访问时才会去请求对应的 chunk。

import { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";

// 只有访问到对应路由时才会加载这个 chunk
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));

export default function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>加载中…</div>}>
        <Routes>
          <Route path="/dashboard" element={<Dashboard />} />
          <Route path="/settings" element={<Settings />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

用 Vite manualChunks 拆分第三方库

路由级懒加载之外,把体积大的第三方库单独拆包,能让它们被浏览器长期缓存,也避免业务代码一改就让用户重新下载整个 vendor。

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          "react-vendor": ["react", "react-dom", "react-router-dom"],
          "chart-vendor": ["echarts"],
        },
      },
    },
  },
});

给最可能的下一步预加载

懒加载会带来短暂白屏。对最可能进入的路由,可以在用户 hover 或空闲时提前 import,把 chunk 悄悄拉到本地。

function preload(routeImport: () => Promise<unknown>) {
  routeImport();
}

// <Link to="/dashboard" onMouseEnter={() => preload(() => import("./pages/Dashboard"))}>仪表盘</Link>

几个常见坑

  • Suspense 的 fallback 必须能正常渲染,且在 SSR 场景下要改用服务端支持的流式方案。
  • 被 lazy 的模块必须默认导出(export default),否则动态 import 拿不到组件。
  • manualChunks 不要切得太碎,chunk 数量过多会变成大量小请求,反而拖慢加载。
  • 懒加载组件内部如果还有数据请求,注意 loading 态要覆盖「代码加载」和「数据加载」两个阶段。
T

test1

文章作者

为什么需要代码分割 单页应用打包后往往是一个巨大的 JS 文件,用户首屏就要下载全部代码,即使很多路由暂时用不到。代码...

分类
技术
发布时间
2026-09-30
字数
约 1811 字
阅读
4 次