Skip to content

Commit

Permalink
docs: Support blog (#38993)
Browse files Browse the repository at this point in the history
* chore: simplify icon

* docs: simplify buttons

* docs: site blog part

* docs: fix trans

* docs: cssinjs

Co-authored-by: MadCcc <1075746765@qq.com>
  • Loading branch information
zombieJ and MadCcc committed Nov 27, 2022
1 parent f118a3f commit e4938e4
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 32 deletions.
4 changes: 0 additions & 4 deletions .dumi/theme/locales/en-US.json
Expand Up @@ -5,10 +5,6 @@
"app.theme.switch.compact": "Compact theme",
"app.header.search": "Search...",
"app.header.menu.documentation": "Docs",
"app.header.menu.development": "Development",
"app.header.menu.components": "Components",
"app.header.menu.spec": "Design",
"app.header.menu.resource": "Resources",
"app.header.menu.more": "More",
"app.header.menu.mobile": "Mobile",
"app.header.menu.pro.v4": "Ant Design Pro",
Expand Down
4 changes: 0 additions & 4 deletions .dumi/theme/locales/zh-CN.json
Expand Up @@ -5,10 +5,6 @@
"app.theme.switch.compact": "紧凑主题",
"app.header.search": "全文本搜索...",
"app.header.menu.documentation": "文档",
"app.header.menu.development": "研发",
"app.header.menu.components": "组件",
"app.header.menu.spec": "设计",
"app.header.menu.resource": "资源",
"app.header.menu.more": "更多",
"app.header.menu.mobile": "移动版",
"app.header.menu.pro.v4": "Ant Design Pro",
Expand Down
57 changes: 48 additions & 9 deletions .dumi/theme/slots/Header/Navigation.tsx
@@ -1,15 +1,35 @@
import * as React from 'react';
import classNames from 'classnames';
import { Link, useLocation, FormattedMessage } from 'dumi';
import type { MenuProps } from 'antd';
import { Link, useLocation, FormattedMessage, useFullSidebarData } from 'dumi';
import { MenuProps, Tooltip } from 'antd';
import { MenuOutlined } from '@ant-design/icons';
import { Menu } from 'antd';
import { getEcosystemGroup } from './More';
import * as utils from '../../utils';
import type { SharedProps } from './interface';
import useSiteToken from '../../../hooks/useSiteToken';
import useLocale from '../../../hooks/useLocale';
import { css } from '@emotion/react';

// ============================= Theme =============================
const locales = {
cn: {
design: '设计',
development: '研发',
components: '组件',
resources: '资源',
blog: '博客',
},
en: {
design: 'Design',
development: 'Development',
components: 'Components',
resources: 'Resources',
blog: 'Blog',
},
};

// ============================= Style =============================
const useStyle = () => {
const { token } = useSiteToken();

Expand Down Expand Up @@ -99,6 +119,10 @@ export default ({
onDirectionChange,
}: NavigationProps) => {
const { pathname, search } = useLocation();
const [locale] = useLocale(locales);

const sidebarData = useFullSidebarData();
const blogList = sidebarData['/docs/blog']?.[0]?.children || [];

const style = useStyle();

Expand Down Expand Up @@ -160,41 +184,56 @@ export default ({
{
label: (
<Link to={utils.getLocalizedPathname('/docs/spec/introduce', isZhCN, search)}>
<FormattedMessage id="app.header.menu.spec" />
{locale.design}
</Link>
),
key: 'docs/spec',
},
{
label: (
<Link to={utils.getLocalizedPathname('/docs/react/introduce', isZhCN, search)}>
<FormattedMessage id="app.header.menu.development" />
{locale.development}
</Link>
),
key: 'docs/react',
},
{
label: (
<Link to={utils.getLocalizedPathname('/components/overview/', isZhCN, search)}>
<FormattedMessage id="app.header.menu.components" />
{locale.components}
</Link>
),
key: 'components',
},
blogList.length
? {
label: (
<Link to={utils.getLocalizedPathname(blogList[0].link, isZhCN, search)}>
{locale.blog}
</Link>
),
key: 'docs/blog',
}
: null,
{
label: (
<Link to={utils.getLocalizedPathname('/docs/resources', isZhCN, search)}>
<FormattedMessage id="app.header.menu.resource" />
{locale.resources}
</Link>
),
key: 'docs/resources',
},
showTechUIButton
? {
label: (
<a href="https://techui.alipay.com" target="__blank" rel="noopener noreferrer">
TechUI
</a>
<Tooltip title="TechUI">
<a href="https://techui.alipay.com" target="__blank" rel="noopener noreferrer">
<img
style={{ width: '1em', height: '1em', transform: `scale(1.8) translateY(-5%)` }}
src="https://gw.alipayobjects.com/zos/hitu-asset/562e1bb6-edd1-4c87-8e5c-fa01b768b7c8/hitu-1617851234091-image.png"
/>
</a>
</Tooltip>
),
key: 'tech-ui',
}
Expand Down
128 changes: 128 additions & 0 deletions .dumi/theme/slots/Header/SwitchBtn.tsx
@@ -0,0 +1,128 @@
import * as React from 'react';
import { Button, Tooltip } from 'antd';
import useSharedStyle from './style';
import useSiteToken from '../../../hooks/useSiteToken';
import { css } from '@emotion/react';

export interface LangBtnProps {
label1: React.ReactNode;
label2: React.ReactNode;
tooltip1?: React.ReactNode;
tooltip2?: React.ReactNode;
value: 1 | 2;
pure?: boolean;
onClick?: React.MouseEventHandler;
}

const useStyle = () => {
const { token } = useSiteToken();
const { controlHeightSM } = token;

return {
btn: css`
padding: 0 !important;
width: ${controlHeightSM}px;
height: ${controlHeightSM}px;
display: inline-flex;
align-items: center;
justify-content: center;
img {
width: 1em;
height: 1em;
}
`,
};
};

export default function LangBtn({
label1,
label2,
tooltip1,
tooltip2,
value,
pure,
onClick,
}: LangBtnProps) {
const { token } = useSiteToken();
const style = useStyle();
const sharedStyle = useSharedStyle();

let label1Style: React.CSSProperties;
let label2Style: React.CSSProperties;

const iconStyle: React.CSSProperties = {
position: 'absolute',
fontSize: '1em',
lineHeight: 1,
border: `1px solid ${token.colorText}`,
color: token.colorText,
};

const fontStyle: React.CSSProperties = {
left: '-5%',
top: 0,
zIndex: 1,
background: token.colorText,
color: token.colorTextLightSolid,
transformOrigin: '0 0',
transform: `scale(0.7)`,
};
const backStyle: React.CSSProperties = {
right: '-5%',
bottom: 0,
zIndex: 0,
transformOrigin: '100% 100%',
transform: `scale(0.5)`,
};

if (value === 1) {
label1Style = fontStyle;
label2Style = backStyle;
} else {
label1Style = backStyle;
label2Style = fontStyle;
}

let node = (
<Button
size="small"
onClick={onClick}
css={[sharedStyle.headerButton, style.btn]}
key="lang-button"
>
{pure ? (
value === 1 ? (
label1
) : (
label2
)
) : (
<div style={{ position: 'relative', width: '1em', height: '1em' }}>
<span
style={{
...iconStyle,
...label1Style,
}}
>
{label1}
</span>
<span
style={{
...iconStyle,
...label2Style,
}}
>
{label2}
</span>
</div>
)}
</Button>
);

if (tooltip1 || tooltip2) {
node = <Tooltip title={value === 1 ? tooltip1 : tooltip2}>{node}</Tooltip>;
}

return node;
}
41 changes: 26 additions & 15 deletions .dumi/theme/slots/Header/index.tsx
Expand Up @@ -19,6 +19,8 @@ import { useLocation, useNavigate } from 'dumi';
import { ClassNames, css } from '@emotion/react';
import useSiteToken from '../../../hooks/useSiteToken';
import useLocale from '../../../hooks/useLocale';
import SwitchBtn from './SwitchBtn';
import useSharedStyle from './style';

const RESPONSIVE_XS = 1120;
const RESPONSIVE_SM = 1200;
Expand Down Expand Up @@ -55,8 +57,8 @@ const useStyle = () => {
}
.nav-search-wrapper {
flex: auto;
display: flex;
flex: auto;
}
.dumi-default-search-bar {
Expand Down Expand Up @@ -121,10 +123,6 @@ const useStyle = () => {
}
}
`,
headerButton: css`
color: ${token.colorText};
border-color: ${token.colorBorder};
`,
popoverMenu: {
width: 300,

Expand Down Expand Up @@ -216,6 +214,7 @@ const Header: React.FC<HeaderProps> = (props) => {
const navigate = useNavigate();

const style = useStyle();
const sharedStyle = useSharedStyle();

const handleHideMenu = useCallback(() => {
setHeaderState((prev) => ({ ...prev, menuVisible: false }));
Expand Down Expand Up @@ -415,17 +414,29 @@ const Header: React.FC<HeaderProps> = (props) => {
{versionOptions}
</Select>
</Popover>,
<Button size="small" onClick={onLangChange} css={style.headerButton} key="lang-button">
<FormattedMessage id="app.header.lang" />
</Button>,
<Button
size="small"
<SwitchBtn
key="lang"
onClick={onLangChange}
value={utils.isZhCN(pathname) ? 1 : 2}
label1="中"
label2="En"
tooltip1="中文 / English"
tooltip2="English / 中文"
/>,
<SwitchBtn
key="direction"
onClick={onDirectionChange}
css={style.headerButton}
key="direction-button"
>
{nextDirectionText}
</Button>,
value={direction === 'rtl' ? 2 : 1}
label1={
<img src="https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*6k0CTJA-HxUAAAAAAAAAAAAADrJ8AQ/original" />
}
tooltip1="LTR"
label2={
<img src="https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*SZoaQqm2hwsAAAAAAAAAAAAADrJ8AQ/original" />
}
tooltip2="RTL"
pure
/>,
<More key="more" {...sharedProps} />,
<Github key="github" responsive={responsive} />,
];
Expand Down
15 changes: 15 additions & 0 deletions .dumi/theme/slots/Header/style.tsx
@@ -0,0 +1,15 @@
import { css } from '@emotion/react';
import useSiteToken from '../../../hooks/useSiteToken';

const useSharedStyle = () => {
const { token } = useSiteToken();

return {
headerButton: css`
color: ${token.colorText};
border-color: ${token.colorBorder};
`,
};
};

export default useSharedStyle;
1 change: 1 addition & 0 deletions .dumi/theme/utils.tsx
Expand Up @@ -139,6 +139,7 @@ export function getLocalizedPathname(
fullPath = pathname.replace(/\/$/, '-cn/');
} else {
fullPath = `${pathname}-cn`;
fullPath = fullPath.replace(/(-cn)+/, '-cn');
}

if (hash) {
Expand Down
26 changes: 26 additions & 0 deletions docs/blog/css-in-js.en-US.md
@@ -0,0 +1,26 @@
---
order: 0
title: Component-level CSS-in-JS
---

On November 18, 2022, we released Ant Design 5.0. At the same time, Ant Design's unique CSS-in-JS solution was brought into everyone's view. Through this solution, Ant Design achieves higher performance than other CSS-in-JS libraries, but at the cost of sacrificing its flexibility for free use in applications. So we call it a "component-level" CSS-in-JS solution. <a name="W668Z"></a>

## Dilemma of CSS-in-JS

In CSS-in-JS, hash is used to confirm whether a style has been inserted. The way to calculate the hash is usually to convert a complete css into a hash value. For example, in emotion, we can see such a style tag by checking the elements on the page. The hash value corresponding to such a style tag is unique:<br />![image.png](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*X5tDQ5VIpcoAAAAAAAAAAAAADrJ8AQ/original)<br />In this way, you can find a problem that CSS-in-JS has been criticized for a long time. What we write when coding is not the final css. So every time we need to serialize to get the css and calculate the hash again. If your page or component has a very complex or a large amount of CSS-in-JS code, and even the style will follow the component's props change, then this performance issue becomes non-negligible.<br />To solve this problem, each CSS-in-JS library will have its own way to deal with it. Let’s take a look at Ant Design’s solution. <a name="Wd3XQ"></a>

## Hash

In fact, it is not difficult for us to find that the problem lies in the process of serializing css. How about reducing the times of serializing css by caching? For application-level CSS-in-JS, it is difficult for us to find a suitable key for cache. But if it is a component library, the final style is relatively stable. <br />According to the style structure we determined from v4 and previous versions, the style of each component will not change under the same theme variable and the same version. Conversely, the style may change only if the theme variable is modified, or the version of antd is changed. <br />From this we get a very simple way to calculate the hash:<br />![image.png](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*XuVYRJ_27Q0AAAAAAAAAAAAADrJ8AQ/original)<br />We will apply the **same** **hash** to all antd components. In this way, when using the antd component, we only perform hash calculations on the current version and theme variables. Version can be obtained directly from `package.json`, and theme variables can be obtained directly from context. So we don't need to serialize css again and again to get a stable hash, and the performance is improved finally. <a name="GxLK1"></a>

## Cache for Components

In the above way, we have taken the first step of "component level" CSS-in-JS, but this is not enough. Since it is "component level", we can also optimize it again with components.<br />In Ant Design, the style of a component is usually complete. That is to say, no matter what variant the component has, its style exist in the whole component style. In this way, we can draw a conclusion again: the props of antd components will not affect the component style. <br />This is very important. In the application-level CSS-in-JS solution, since props may affect the component style, it is inevitable that the component style will be regenerated during the rendering phase. No matter how to optimize this point, it cannot be ignored. Now that we have adopted a "component-level" solution, this problem can be easily solved: do style caching for components.<br />![image.png](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*yZMNSYVtxnAAAAAAAAAAAAAADrJ8AQ/original)<br />In the case of the same hash, no matter how many times the same component is used and rendered, the style will only be generated once at the first mount, and will hit the cache for the rest of the time. This is the second insurance for "component level" CSS-in-JS solutions. <a name="DUbKx"></a>

## Benchmark

At the release of Ant Design 5.0, we simply made a benchmark, and here are some supplementary instructions:<br />![image.png](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*upmYSqZ5FwsAAAAAAAAAAAAADrJ8AQ/original)<br />The benchmark is based on generating a very long unchanging style to test the performance of basic usage of the three libraries. It can be seen that under the "component level" usage scenario of Ant Design, @ant-design/cssinjs has a performance advantage whether it is the first rendering or the second rendering. Since styled has certain optimizations when dealing with stable styles, the performance of secondary rendering in this benchmark is better, but it will still be affected by recalculation like emotion when props participate in style calculation. <a name="JOmkZ"></a>

## Limitation

In the above comparison, it cannot be said that antd is definitely better than styled and emotion, but in the component-level usage scenarios, we have made corresponding optimizations to obtain performance advantages. Conversely, due to the limitation of "component level", antd's CSS-in-JS solution is not suitable for construction applications.<br />Due to the special hash calculation method and component cache, when applying antd's CSS-in-JS solution, developers must provide stable hash and unique component names by themselves. For applications, automatic hash capabilities such as css modules are more needed. At the same time, caching a large number of components in the application also requires additional management costs. Once an error occurs, it is difficult to troubleshoot. Therefore, we recommend using the "component-level" CSS-in-JS solution in component libraries.

0 comments on commit e4938e4

Please sign in to comment.