在 Next.js 中使用 Ant Design

66 min read

Next.js 是目前世界上最受欢迎的 React 服务端同构框架。本文将介绍如何在 Next.js 创建的项目中使用 Ant Design 组件。

安装与初始化

在开始之前,你需要安装 yarn、pnpm 或 bun。

# 使用 pnpm 创建 Next.js 项目
$ pnpm create next-app antd-demo

该工具会自动创建并初始化环境和依赖。如果在过程中遇到网络问题,请尝试配置代理设置或使用其他 npm registry。

初始化完成后,进入项目并启动开发服务器。

$ cd antd-demo
$ npm run dev

在浏览器中打开 http://localhost:3000/,如果看到 NEXT 的 logo,表示成功。

引入 Ant Design

现在,我们使用 yarn 或 npm 或 pnpm 或 bun 安装 antd。

# 安装 antd
$ npm install antd --save

修改 src/app/page.tsx,引入 antd 的 Button 组件。

import React from 'react';
import { Button } from 'antd';

const Home = () => (
  <div className="App">
    <Button type="primary">Button</Button>
  </div>
);

export default Home;

此时,你应该可以在页面上看到一个蓝色的 primary 按钮。接下来,你可以选择 antd 的任何组件来开发你的应用。

使用 App Router 模式

如果你在 Next.js 中使用 App Router 并且使用 antd 作为你的组件库,为了更好地在 Next.js 应用中使用 antd 组件库,并提供更好的用户体验,可以尝试使用以下方法将 antd 的首屏样式提取并注入 HTML,以避免页面闪烁。

安装 @ant-design/nextjs-registry

# 安装 @ant-design/nextjs-registry
$ npm install @ant-design/nextjs-registry --save

app/layout.tsx 中使用它

import React from 'react';
import { AntdRegistry } from '@ant-design/nextjs-registry';

const RootLayout = ({ children }: React.PropsWithChildren) => (
  <html lang="en">
    <body>
      <AntdRegistry>{children}</AntdRegistry>
    </body>
  </html>
);

export default RootLayout;

注意:Next.js App Router 目前不支持通过 . 方式使用子组件,比如 <Select.Option /><Typography.Text />。可以通过路径导入解决此问题。

使用 Pages Router 模式

如果你在 Next.js 中使用 Pages Router 并且使用 antd 作为你的组件库,为了更好地在 Next.js 应用中使用 antd 组件库,并提供更好的用户体验,可以尝试使用以下方法将 antd 的首屏样式提取并注入 HTML,以避免页面闪烁。

安装 @ant-design/cssinjs

请注意,安装 @ant-design/cssinjs 时,必须确保其版本与 antd 本地 node_modules 中的 @ant-design/cssinjs 版本一致,否则会出现多个 React 实例,导致 ctx 无法正确读取的问题。

# 安装 @ant-design/cssinjs
$ npm install @ant-design/cssinjs --save

重写 pages/_document.tsx

import React from 'react';
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import Document, { Head, Html, Main, NextScript } from 'next/document';
import type { DocumentContext } from 'next/document';

const MyDocument = () => (
  <Html lang="en">
    <Head />
    <body>
      <Main />
      <NextScript />
    </body>
  </Html>
);

MyDocument.getInitialProps = async (ctx: DocumentContext) => {
  const cache = createCache();
  const originalRenderPage = ctx.renderPage;
  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) => (
        <StyleProvider cache={cache}>
          <App {...props} />
        </StyleProvider>
      ),
    });

  const initialProps = await Document.getInitialProps(ctx);
  const style = extractStyle(cache, true);
  return {
    ...initialProps,
    styles: (
      <>
        {initialProps.styles}
        <style dangerouslySetInnerHTML={{ __html: style }} />
      </>
    ),
  };
};

export default MyDocument;

支持自定义主题

theme/themeConfig.ts

import type { ThemeConfig } from 'antd';

const theme: ThemeConfig = {
  token: {
    fontSize: 16,
    colorPrimary: '#52c41a',
  },
};

export default theme;

重写 pages/_app.tsx

import React from 'react';
import { ConfigProvider } from 'antd';
import type { AppProps } from 'next/app';

import theme from './theme/themeConfig';

const App = ({ Component, pageProps }: AppProps) => (
  <ConfigProvider theme={theme}>
    <Component {...pageProps} />
  </ConfigProvider>
);

export default App;

在页面组件中使用 antd

import React from 'react';
import { Button } from 'antd';

const Home = () => (
  <div className="App">
    <Button type="primary">Button</Button>
  </div>
);

export default Home;

更多详细信息,请参考 with-nextjs-inline-style.