Ant Design React 服务端渲染
2023-09-27 11:01 更新
服务端渲染样式有两种方案,它们各有优缺点:
- 内联样式:在渲染时无需额外请求样式文件,好处是减少额外的网络请求,缺点则是会使得 HTML 体积增大,影响首屏渲染速度,相关讨论参考:#39891
- 整体导出:提前烘焙 antd 组件样式为 css 文件,在页面中时引入。好处是打开任意页面时如传统 css 方案一样都会复用同一套 css 文件以命中缓存,缺点是如果页面中存在多主题,则需要额外进行烘焙
内联样式
使用 @ant-design/cssinjs
将所需样式抽离:
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import { renderToString } from 'react-dom/server';
export default () => {
// SSR Render
const cache = createCache();
const html = renderToString(
<StyleProvider cache={cache}>
<MyApp />
</StyleProvider>,
);
// Grab style from cache
const styleText = extractStyle(cache);
// Mix with style
return `
<!DOCTYPE html>
<html>
<head>
${styleText}
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`;
};
整体导出
如果你想要将样式文件抽离到 css 文件中,可以尝试使用以下方案:
- 安装依赖
npm install ts-node tslib cross-env --save-dev
- 新增
tsconfig.node.json
文件
{
"compilerOptions": {
"strictNullChecks": true,
"module": "NodeNext",
"jsx": "react",
"esModuleInterop": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}
- 新增
scripts/genAntdCss.tsx
文件
// scripts/genAntdCss.tsx
import fs from 'fs';
import { extractStyle } from '@ant-design/static-style-extract';
const outputPath = './public/antd.min.css';
const css = extractStyle();
fs.writeFileSync(outputPath, css);
若你想使用混合主题或自定义主题,可采用以下脚本:
import fs from 'fs';
import { extractStyle } from '@ant-design/static-style-extract';
import React from 'react';
import { ConfigProvider } from 'antd';
const outputPath = './public/antd.min.css';
const testGreenColor = '#008000';
const testRedColor = '#ff0000';
const css = extractStyle((node) => (
<>
<ConfigProvider
theme={{
token: {
colorBgBase: testGreenColor,
},
}}
>
{node}
</ConfigProvider>
<ConfigProvider
theme={{
token: {
colorPrimary: testGreenColor,
},
}}
>
<ConfigProvider
theme={{
token: {
colorBgBase: testRedColor,
},
}}
>
{node}
</ConfigProvider>
</ConfigProvider>
</>
));
fs.writeFileSync(outputPath, css);
你可以选择在启动开发命令或编译前执行这个脚本,运行上述脚本将会在当前项目的指定(如: public 目录)目录下直接生成一个全量的 antd.min.css 文件。
以 Next.js 为例(参考示例):
// package.json
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"predev": "ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx",
"prebuild": "cross-env NODE_ENV=production ts-node --project ./tsconfig.node.json ./scripts/genAntdCss.tsx"
}
}
然后,你只需要在pages/_app.tsx
文件中引入这个文件即可:
import { StyleProvider } from '@ant-design/cssinjs';
import type { AppProps } from 'next/app';
import '../public/antd.min.css'; // 添加这行
import '../styles/globals.css';
export default function App({ Component, pageProps }: AppProps) {
return (
<StyleProvider hashPriority="high">
<Component {...pageProps} />
</StyleProvider>
);
}
自定义主题
如果你的项目中使用了自定义主题,可以尝试通过以下方式进行烘焙:
import { extractStyle } from '@ant-design/static-style-extract';
import { ConfigProvider } from 'antd';
const cssText = extractStyle((node) => (
<ConfigProvider
theme={{
token: {
colorPrimary: 'red',
},
}}
>
{node}
</ConfigProvider>
));
混合主题
如果你的项目中使用了混合主题,可以尝试通过以下方式进行烘焙:
import { extractStyle } from '@ant-design/static-style-extract';
import { ConfigProvider } from 'antd';
const cssText = extractStyle((node) => (
<>
<ConfigProvider
theme={{
token: {
colorBgBase: 'green ',
},
}}
>
{node}
</ConfigProvider>
<ConfigProvider
theme={{
token: {
colorPrimary: 'blue',
},
}}
>
<ConfigProvider
theme={{
token: {
colorBgBase: 'red ',
},
}}
>
{node}
</ConfigProvider>
</ConfigProvider>
</>
));
更多static-style-extract
的实现细节请看:static-style-extract。
按需抽取
// scripts/genAntdCss.tsx
import { createHash } from 'crypto';
import fs from 'fs';
import path from 'path';
import type Entity from '@ant-design/cssinjs/lib/Cache';
import { extractStyle } from '@ant-design/cssinjs';
export type DoExtraStyleOptions = {
cache: Entity;
dir?: string;
baseFileName?: string;
};
export function doExtraStyle({
cache,
dir = 'antd-output',
baseFileName = 'antd.min',
}: DoExtraStyleOptions) {
const baseDir = path.resolve(__dirname, '../../static/css');
const outputCssPath = path.join(baseDir, dir);
if (!fs.existsSync(outputCssPath)) {
fs.mkdirSync(outputCssPath, { recursive: true });
}
const css = extractStyle(cache, true);
if (!css) return '';
const md5 = createHash('md5');
const hash = md5.update(css).digest('hex');
const fileName = `${baseFileName}.${hash.substring(0, 8)}.css`;
const fullpath = path.join(outputCssPath, fileName);
const res = `_next/static/css/${dir}/${fileName}`;
if (fs.existsSync(fullpath)) return res;
fs.writeFileSync(fullpath, css);
return res;
}
在 _document.tsx
中使用上述工具进行按需导出:
// _document.tsx
import { StyleProvider, createCache } from '@ant-design/cssinjs';
import type { DocumentContext } from 'next/document';
import Document, { Head, Html, Main, NextScript } from 'next/document';
import { doExtraStyle } from '../scripts/genAntdCss';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const cache = createCache();
let fileName = '';
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => (
<StyleProvider cache={cache}>
<App {...props} />
</StyleProvider>
),
});
const initialProps = await Document.getInitialProps(ctx);
// 1.1 extract style which had been used
fileName = doExtraStyle({
cache,
});
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{/* 1.2 inject css */}
{fileName && <link rel="stylesheet" href={`/${fileName}`} />}
</>
),
};
}
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
演示示例请看:按需抽取样式示例
以上内容是否对您有帮助:
更多建议: