Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

基于 antd-style 重构 ssr 实现 #17

Merged
merged 3 commits into from
Mar 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
]
},
"dependencies": {
"@ant-design/cssinjs": "^1",
"@babel/runtime": "^7",
"@emotion/css": "^11",
"@emotion/server": "^11",
Expand Down
18 changes: 15 additions & 3 deletions src/components/DumiSiteProvider/ThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { CustomTokenParams, StyleProvider, ThemeProvider as Provider } from 'antd-style';
import {
CustomTokenParams,
extractStaticStyle,
StyleProvider,
ThemeProvider as Provider,
} from 'antd-style';
import { ReactNode, useCallback } from 'react';

import { useThemeStore } from '../../store/useThemeStore';
Expand All @@ -8,9 +13,11 @@ import { SiteConfigToken } from '../../types';
export interface ThemeProviderProps {
token?: Partial<SiteConfigToken>;
children?: ReactNode;
ssrInline?: boolean;
cache?: typeof extractStaticStyle.cache;
}

export const ThemeProvider = ({ children, token }: ThemeProviderProps) => {
export const ThemeProvider = ({ children, token, ssrInline, cache }: ThemeProviderProps) => {
const themeMode = useThemeStore((s) => s.themeMode);

const getCustomToken = useCallback(
Expand All @@ -27,7 +34,12 @@ export const ThemeProvider = ({ children, token }: ThemeProviderProps) => {
);

return (
<StyleProvider speedy={process.env.NODE_ENV === 'production'} prefix={'site'}>
<StyleProvider
speedy={process.env.NODE_ENV === 'production'}
prefix={'site'}
cache={cache}
ssrInline={ssrInline}
>
<Provider
prefixCls={'site'}
themeMode={themeMode}
Expand Down
11 changes: 4 additions & 7 deletions src/components/DumiSiteProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { App } from 'antd';
import { StyleProvider } from 'antd-style';

import { ThemeProvider, ThemeProviderProps } from './ThemeProvider';

export default ({ children, token }: ThemeProviderProps) => {
export default ({ children, ...props }: ThemeProviderProps) => {
return (
<StyleProvider speedy={process.env.NODE_ENV === 'production'} prefix={'site'}>
<ThemeProvider token={token}>
<App>{children}</App>
</ThemeProvider>
</StyleProvider>
<ThemeProvider {...props}>
<App>{children}</App>
</ThemeProvider>
);
};

Expand Down
16 changes: 12 additions & 4 deletions src/layouts/DocLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import animateScrollTo from 'animated-scroll-to';
import { extractStaticStyle } from 'antd-style';
import { Helmet, useIntl, useLocation } from 'dumi';
import isEqual from 'fast-deep-equal';
import { memo, PropsWithChildren, StrictMode, useEffect, type FC } from 'react';
Expand Down Expand Up @@ -53,17 +54,24 @@ const DocLayout: FC = memo(() => {
);
});

const THemeProvider = ({ children }: PropsWithChildren) => {
// @ts-ignore
global.__ANTD_CACHE__ = extractStaticStyle.cache;

const ThemeProvider = ({ children }: PropsWithChildren) => {
const siteToken = useSiteStore((s) => s.siteData.themeConfig.siteToken, isEqual);
return <DumiSiteProvider token={siteToken}>{children}</DumiSiteProvider>;
return (
<DumiSiteProvider cache={extractStaticStyle.cache} token={siteToken}>
{children}
</DumiSiteProvider>
);
};

export default () => (
<StrictMode>
<StoreUpdater />
<THemeProvider>
<ThemeProvider>
<GlobalStyle />
<DocLayout />
</THemeProvider>
</ThemeProvider>
</StrictMode>
);
13 changes: 0 additions & 13 deletions src/layouts/GlobalLayout/index.tsx

This file was deleted.

72 changes: 13 additions & 59 deletions src/plugin/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import { extractStyle } from '@ant-design/cssinjs';
import createEmotionServer from '@emotion/server/create-instance';
import { EmotionCache } from '@emotion/utils';
import { CacheManager } from 'antd-style';
import { extractStaticStyle } from 'antd-style';
import chalk from 'chalk';
import type { IApi } from 'dumi';
import fs from 'fs';
import { join } from 'path';

import { getHash } from './utils';

declare global {
// eslint-disable-next-line no-var
var __ANTD_STYLE_CACHE_MANAGER_FOR_SSR__: CacheManager;
}

/*
* SSR 抽取样式
*/
Expand All @@ -28,7 +20,7 @@ const SSRPlugin = (api: IApi) => {
api.logger.info('detect ssr config, when building html will extract css.');

const writeCSSFile = (key: string, hashKey: string, cssString: string) => {
const fileName = `ssr-${key}-${getHash(hashKey)}.css`;
const fileName = `ssr-${key}.${getHash(hashKey)}.css`;

const filePath = join(api.paths.absOutputPath, fileName);

Expand All @@ -40,36 +32,6 @@ const SSRPlugin = (api: IApi) => {
return fileName;
};

const getStyleFromEmotionCache = (
cache: EmotionCache,
file: {
content: string;
path: string;
},
) => {
const result = createEmotionServer(cache).extractCritical(file.content);

const css = result.css ?? '';

if (!!css) {
api.logger.event(
`${chalk.yellow(file.path)} include ${chalk.blue`[${cache.key}]`} ${chalk.yellow(
result.ids.length,
)} styles`,
);

const cssFile = writeCSSFile(cache.key, result.ids.join(''), css);

const tag = `<style data-emotion="${cache.key} ${result.ids.join(' ')}">${
result.css
}</style>`;

return { css, file: cssFile, tag };
}

return {};
};

const addLinkStyle = (html: string, cssFile: string) => {
const prefix = api.userConfig.publicPath || api.config.publicPath;
return html.replace('</head>', `<link rel="stylesheet" href="${prefix + cssFile}"></head>`);
Expand All @@ -81,29 +43,21 @@ const SSRPlugin = (api: IApi) => {
.filter((f) => !f.path.includes(':'))

.map((file) => {
// 提取 antd 样式
const styleCache = (global as any).__ANTD_CACHE__;

const styleText = styleCache ? extractStyle(styleCache) : '';

const antdCssString = styleText.replace(/<style\s[^>]*>/g, '').replace(/<\/style>/g, '');

if (antdCssString) {
api.logger.event(`${chalk.yellow(file.path)} include ${chalk.blue`antd`} styles`);
const antdCssFileName = writeCSSFile('antd', antdCssString, antdCssString);
file.content = addLinkStyle(file.content, antdCssFileName);
}
const antdCache = (global as any).__ANTD_CACHE__;

// 提取 antd-style emotion 样式
// 提取 antd-style 样式到独立 css 文件
const styles = extractStaticStyle(file.content, { antdCache });

const cacheManager = global.__ANTD_STYLE_CACHE_MANAGER_FOR_SSR__;
styles.forEach((result) => {
api.logger.event(
`${chalk.yellow(file.path)} include ${chalk.blue`[${result.key}]`} ${chalk.yellow(
result.ids.length,
)} styles`,
);

cacheManager.getCacheList().forEach((cache) => {
const styleFromCache = getStyleFromEmotionCache(cache, file);
const cssFile = writeCSSFile(result.key, result.ids.join(''), result.css);

if (styleFromCache.file) {
file.content = addLinkStyle(file.content, styleFromCache.file);
}
file.content = addLinkStyle(file.content, cssFile);
});

return file;
Expand Down
3 changes: 2 additions & 1 deletion src/plugin/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createHash } from 'crypto';

export const getHash = (str: string) => createHash('md5').update(str).digest('base64url');
export const getHash = (str: string, length = 8) =>
createHash('md5').update(str).digest('hex').slice(0, length);