服务端渲染
服务端渲染
服务端渲染样式有两种方案,它们各有优缺点:
使用 @ant-design/cssinjs
将所需样式抽离:
import React from 'react';import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';import type Entity from '@ant-design/cssinjs/es/Cache';import { renderToString } from 'react-dom/server';const App = () => {// SSR Renderconst cache = React.useMemo<Entity>(() => createCache(), []);const html = renderToString(<StyleProvider cache={cache}><MyApp /></StyleProvider>,);// Grab style from cacheconst styleText = extractStyle(cache);// Mix with stylereturn `<!DOCTYPE html><html><head>${styleText}</head><body><div id="root">${html}</div></body></html>`;};export default App;
如果你想要将样式文件抽离到 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.tsximport 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 React from 'react';import { extractStyle } from '@ant-design/static-style-extract';import { ConfigProvider } from 'antd';const outputPath = './public/antd.min.css';const testGreenColor = '#008000';const testRedColor = '#ff0000';const css = extractStyle((node) => (<><ConfigProvidertheme={{token: {colorBgBase: testGreenColor,},}}>{node}</ConfigProvider><ConfigProvidertheme={{token: {colorPrimary: testGreenColor,},}}><ConfigProvidertheme={{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) => (<ConfigProvidertheme={{token: {colorPrimary: 'red',},}}>{node}</ConfigProvider>));
如果你的项目中使用了混合主题,可以尝试通过以下方式进行烘焙:
import { extractStyle } from '@ant-design/static-style-extract';import { ConfigProvider } from 'antd';const cssText = extractStyle((node) => (<><ConfigProvidertheme={{token: {colorBgBase: 'green ',},}}>{node}</ConfigProvider><ConfigProvidertheme={{token: {colorPrimary: 'blue',},}}><ConfigProvidertheme={{token: {colorBgBase: 'red ',},}}>{node}</ConfigProvider></ConfigProvider></>));
更多static-style-extract
的实现细节请看:static-style-extract。
// scripts/genAntdCss.tsximport { createHash } from 'crypto';import fs from 'fs';import path from 'path';import { extractStyle } from '@ant-design/cssinjs';import type Entity from '@ant-design/cssinjs/lib/Cache';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.tsximport { createCache, StyleProvider } 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 usedfileName = 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>);}}
演示示例请看:按需抽取样式示例