next/dynamic和React.lazy的区别

0 阅读3分钟

🎯 核心结论:一个是“官方底料”,一个是“豪华套餐”

简单来说:next/dynamic 是 Next.js 基于 React.lazy() 封装的“增强版”懒加载方案。官方文档的原话是:“next/dynamic is a composite of React.lazy() and Suspense”。

你可以这样理解:

  • React.lazy() = 毛坯房(React官方提供的基础能力)
  • next/dynamic = 精装房(Next.js帮你配好了家具、水电、甚至智能家居)

📊 一图看懂核心区别

对比维度React.lazy()next/dynamic
所属生态React 官方Next.js 专属
本质底层懒加载 APIReact.lazy() + Suspense 的封装
加载状态必须配合 <Suspense fallback={...}> 使用内置 loading 参数,无需额外写 Suspense
SSR 控制❌ 无法单独控制(服务端会尝试渲染)✅ 支持 ssr: false 禁用服务端渲染
命名导出支持❌ 仅支持 default 导出✅ 支持命名导出(通过 .then(mod => mod.xxx)
预加载支持❌ 无内置✅ Next.js 会自动预加载相关的 webpack 块
数据加载集成需要额外配合 Suspense 处理数据请求可以配合 Suspense 实现“组件加载 + 数据加载”的统一 fallback

🔍 详细拆解:三个关键差异

1️⃣ 加载状态的写法

React.lazy():必须手动写 <Suspense>

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

next/dynamic:内置 loading,少写一层嵌套

import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <div>加载中...</div>,  // ← 直接在这里配置
});

function App() {
  return <DynamicComponent />;  // ← 不需要额外包裹 Suspense
}

2️⃣ SSR 控制(这是最关键的差异!)

有些组件依赖浏览器 API(比如 windowlocalStorage),在服务端渲染时会报错。

React.lazy():没有解决方案。组件在服务端依然会被执行,除非你写一堆 typeof window !== 'undefined' 的判断。

next/dynamic:一键关闭 SSR

const ClientOnlyComponent = dynamic(() => import('./UsesWindowAPI'), {
  ssr: false,  // ← 这个组件永远不会在服务端渲染
  loading: () => <div>客户端加载中...</div>
});

这样组件只在浏览器里加载,彻底解决 window is not defined 的问题。

3️⃣ 命名导出 vs 默认导出

React.lazy():组件必须是 export default,否则会报错

// ❌ 这样不行
export function MyComponent() { ... }

// ✅ 必须这样
export default function MyComponent() { ... }

next/dynamic:支持命名导出,灵活多了

// components/hello.js
export function Hello() {
  return <p>Hello!</p>
}

// pages/index.js
const DynamicHello = dynamic(() => 
  import('../components/hello').then((mod) => mod.Hello)  // ← 直接拿命名导出
);

🧠 进阶用法:当它们“组队作战”

很多人以为有了 next/dynamic 就不需要 React.lazy() 了,其实不是。它们可以组合使用,实现更细腻的加载体验:

'use client';

import { Suspense } from 'react';
import dynamic from 'next/dynamic';

// 1. next/dynamic 负责组件代码的懒加载
const GuestUserPantry = dynamic(() => import('./GuestUserPantry'), {
  ssr: false,
  loading: () => <Skeleton />,  // 组件下载时的 fallback
});

export default function Pantry() {
  return (
    // 2. Suspense 负责组件内部数据请求的 fallback
    <Suspense fallback={<Skeleton />}>
      <GuestUserPantry />
    </Suspense>
  );
}

这样做的好处:不管是在等组件下载,还是在等组件内部的数据请求,用户看到的都是同一个骨架屏,不会出现“闪两次加载”的糟糕体验。


✅ 实战选择指南

你的场景推荐方案原因
Next.js 项目 + 需要 SSR 控制next/dynamic能关闭 SSR,天然适配 Next.js 生态
Next.js 项目 + 简单懒加载next/dynamic语法更简洁,自动预加载
纯 React / Vite / CRA 项目React.lazy()没有 Next.js,只能用这个
需要懒加载命名导出的组件next/dynamicReact.lazy 不支持
组件内部有数据请求next/dynamic + <Suspense>两个配合,统一 loading 状态
依赖浏览器 API 的组件next/dynamic + ssr: false这是 React.lazy 完全做不到的

💡 一句话总结

React.lazy() 是 React 官方给的“基础工具包”,而 next/dynamic 是 Next.js 在这个工具包上加了“电动螺丝刀、激光测距仪、自带照明”——专治各种懒加载场景下的疑难杂症,尤其是在 SSR 控制方面。

如果只是简单的代码分割,两者差别不大;一旦涉及到SSR 兼容性、命名导出、统一加载状态这些真实项目必遇的问题,next/dynamic 的优势就体现出来了。