mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
docs: Update Blog (#39399)
* docs: auto order * docs: getContainer blog * chore: rm console
This commit is contained in:
parent
b2df64a057
commit
33deda5e38
@ -115,8 +115,14 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const list = group.children || [];
|
||||||
|
// 如果有 date 字段,我们就对其进行排序
|
||||||
|
if (list.every((info) => info?.frontmatter?.date)) {
|
||||||
|
list.sort((a, b) => (a.frontmatter.date > b.frontmatter.date ? -1 : 1));
|
||||||
|
}
|
||||||
|
|
||||||
result.push(
|
result.push(
|
||||||
...(group.children?.map((item) => ({
|
...list.map((item) => ({
|
||||||
label: (
|
label: (
|
||||||
<Link to={`${item.link}${search}`}>
|
<Link to={`${item.link}${search}`}>
|
||||||
{before}
|
{before}
|
||||||
@ -125,7 +131,7 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
|
|||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
key: item.link.replace(/(-cn$)/g, ''),
|
key: item.link.replace(/(-cn$)/g, ''),
|
||||||
})) ?? []),
|
})),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useMemo, useState, useLayoutEffect, useContext } from 'react';
|
import React, { useMemo, useState, useLayoutEffect, useContext } from 'react';
|
||||||
import { useIntl, useRouteMeta, FormattedMessage } from 'dumi';
|
import { useIntl, useRouteMeta, FormattedMessage } from 'dumi';
|
||||||
import { Col, Typography, Avatar, Tooltip, Affix, Anchor } from 'antd';
|
import { Col, Typography, Avatar, Tooltip, Affix, Anchor, Space } from 'antd';
|
||||||
|
import { CalendarOutlined } from '@ant-design/icons';
|
||||||
import ContributorsList from '@qixian.cs/github-contributors-list';
|
import ContributorsList from '@qixian.cs/github-contributors-list';
|
||||||
|
import DayJS from 'dayjs';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Footer from '../Footer';
|
import Footer from '../Footer';
|
||||||
@ -183,6 +185,25 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
|
|
||||||
|
{/* 添加作者、时间等信息 */}
|
||||||
|
{meta.frontmatter.date || meta.frontmatter.author ? (
|
||||||
|
<Typography.Paragraph style={{ opacity: 0.65 }}>
|
||||||
|
<Space>
|
||||||
|
{meta.frontmatter.date && (
|
||||||
|
<span>
|
||||||
|
<CalendarOutlined /> {DayJS(meta.frontmatter.date).format('YYYY-MM-DD')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{meta.frontmatter.author && (
|
||||||
|
<Typography.Link href={`https://github.com/${meta.frontmatter.author}`}>
|
||||||
|
@{meta.frontmatter.author}
|
||||||
|
</Typography.Link>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</Typography.Paragraph>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
{meta.frontmatter.filename && (
|
{meta.frontmatter.filename && (
|
||||||
<ContributorsList
|
<ContributorsList
|
||||||
|
@ -206,7 +206,13 @@ export default ({
|
|||||||
blogList.length
|
blogList.length
|
||||||
? {
|
? {
|
||||||
label: (
|
label: (
|
||||||
<Link to={utils.getLocalizedPathname(blogList[0].link, isZhCN, search)}>
|
<Link
|
||||||
|
to={utils.getLocalizedPathname(
|
||||||
|
blogList.sort((a, b) => (a.frontmatter.date > b.frontmatter.date ? -1 : 1))[0].link,
|
||||||
|
isZhCN,
|
||||||
|
search,
|
||||||
|
)}
|
||||||
|
>
|
||||||
{locale.blog}
|
{locale.blog}
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
order: 0
|
|
||||||
title: Component-level CSS-in-JS
|
title: Component-level CSS-in-JS
|
||||||
|
date: 2022-11-25
|
||||||
|
author: MadCcc
|
||||||
---
|
---
|
||||||
|
|
||||||
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>
|
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>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
---
|
---
|
||||||
order: 0
|
|
||||||
title: 组件级别的 CSS-in-JS
|
title: 组件级别的 CSS-in-JS
|
||||||
|
date: 2022-11-25
|
||||||
|
author: MadCcc
|
||||||
---
|
---
|
||||||
|
|
||||||
`2022-11-25`
|
|
||||||
|
|
||||||
在 2022 年 11 月 18 日,我们发布了 Ant Design 5.0 的正式版本,同时带入大家视野中的还有 Ant Design 独特的 CSS-in-JS 方案。通过这个方案,Ant Design 获得了相较于其他 CSS-in-JS 库更高的性能,但代价则是牺牲了其在应用中自由使用的灵活性。所以我们把它称为“组件级”的 CSS-in-JS 方案。
|
在 2022 年 11 月 18 日,我们发布了 Ant Design 5.0 的正式版本,同时带入大家视野中的还有 Ant Design 独特的 CSS-in-JS 方案。通过这个方案,Ant Design 获得了相较于其他 CSS-in-JS 库更高的性能,但代价则是牺牲了其在应用中自由使用的灵活性。所以我们把它称为“组件级”的 CSS-in-JS 方案。
|
||||||
|
|
||||||
## CSS-in-JS 的困境
|
## CSS-in-JS 的困境
|
||||||
|
113
docs/blog/getContainer.en-US.md
Normal file
113
docs/blog/getContainer.en-US.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
title: Some change on getContainer
|
||||||
|
date: 2022-12-08
|
||||||
|
author: zombieJ
|
||||||
|
---
|
||||||
|
|
||||||
|
We often encounter the need for pop-up elements when developing, such as the Select drop-down box, or the Modal component. When it is directly rendered under the current node, it may be clipped by the `overflow: hidden` of the parent node:
|
||||||
|
|
||||||
|
![Overflow](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Noh-TYJ0BdcAAAAAAAAAAAAADrJ8AQ/original)
|
||||||
|
|
||||||
|
Therefore we render it under `body` by default in Ant Design, but this will bring new problems. Since they are not under the same container, when the user scrolls the screen, they will find that the popup layer does not follow the scrolling:
|
||||||
|
|
||||||
|
![Scroll](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d44KQqkTX90AAAAAAAAAAAAADrJ8AQ/original)
|
||||||
|
|
||||||
|
To solve this problem, we provide the `getContainer` property, which allows users to customize the rendered container. The `getContainer` method will be called when the component is mounted, returning a container node, and the component will be rendered under this node through `createPortal`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const PopupWrapper = () => {
|
||||||
|
const eleRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// It's much complex with timing in real world. You can view the source for more detail:
|
||||||
|
// https://github.com/react-component/portal/blob/master/src/Portal.tsx
|
||||||
|
const container: HTMLElement = getContainer(eleRef.current);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={eleRef}>
|
||||||
|
{...}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const defaultGetContainer = () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
document.body.appendChild(div);
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SomeComponent = ({ getContainer = defaultGetContainer }) => (
|
||||||
|
<PopupWrapper getContainer={getContainer} />
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
For the time being, we don’t pay attention to `getContainer`’s need to dynamically switch the mount node (in fact, it has not been able to switch for a long time in the past), only from the perspective of React 18, it has encountered some problems.
|
||||||
|
|
||||||
|
## React 18 Concurrent Mode
|
||||||
|
|
||||||
|
In React 18, effects may fire multiple times. In order to prevent inadvertently breaking the developer's behavior, it has also been adjusted accordingly under [StrictMode](https://reactjs.org/docs/strict-mode.html):
|
||||||
|
|
||||||
|
> - React mounts the component.
|
||||||
|
> - Layout effects are created.
|
||||||
|
> - Effect effects are created.
|
||||||
|
> - React simulates effects being destroyed on a mounted component.
|
||||||
|
> - Layout effects are destroyed.
|
||||||
|
> - Effects are destroyed.
|
||||||
|
> - React simulates effects being re-created on a mounted component.
|
||||||
|
> - Layout effects are created
|
||||||
|
> - Effect setup code runs
|
||||||
|
|
||||||
|
The simple understanding is that under StrictMode, even if your deps contains empty objects, the effect will still be triggered multiple times. When switching to React 18 StrictMode, we will find that there will be a pair of mount nodes in the HTML, and the previous one is empty:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body>
|
||||||
|
<div id="root">...</div>
|
||||||
|
|
||||||
|
<!-- Empty -->
|
||||||
|
<div className="sample-holder"></div>
|
||||||
|
|
||||||
|
<!-- Real in use -->
|
||||||
|
<div className="sample-holder">
|
||||||
|
<div className="ant-component-wrapper">...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
Therefore, we adjusted the call implementation, and the default `getContainer` is also managed through state to ensure that the nodes generated by the previous effect will be cleaned up in StrictMode:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const SomeComponent = ({ getContainer }) => {
|
||||||
|
const [myContainer, setMyContainer] = React.useState<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (getContainer) {
|
||||||
|
setMyContainer(getContainer());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
document.body.appendChild(div);
|
||||||
|
setMyContainer(div);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
};
|
||||||
|
}, [getContainer]);
|
||||||
|
|
||||||
|
return <PopupWrapper getContainer={() => myContainer} />;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
After putting `getContainer` into effect management, we can manage nodes in a way that is more in line with the React life cycle, and we can also clean up when `getContainer` changes. So as to support the scenario of dynamically changing `getContainer` (although I personally doubt the universality of this usage scenario).
|
||||||
|
|
||||||
|
## Finally
|
||||||
|
|
||||||
|
Due to the fix that `getContainer` does not support dynamic changes, it also introduces a potential breaking change at the same time. If the developer customizes `getContainer` to create a new dom node every time, it will cause an infinite loop because of the continuous execution of the effect, resulting in the continuous creation of nodes. If you use this method and encounter problems, you need to pay attention to check.
|
113
docs/blog/getContainer.zh-CN.md
Normal file
113
docs/blog/getContainer.zh-CN.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
---
|
||||||
|
title: getContainer 的一些变化
|
||||||
|
date: 2022-12-08
|
||||||
|
author: zombieJ
|
||||||
|
---
|
||||||
|
|
||||||
|
在网页开发中,我们时长会遇到弹出元素的需求,比如 Select 的下拉框、或者是 Modal 组件。直接将其渲染到当前节点下时,可能会被父节点的 `overflow: hidden` 裁剪掉:
|
||||||
|
|
||||||
|
![Overflow](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*Noh-TYJ0BdcAAAAAAAAAAAAADrJ8AQ/original)
|
||||||
|
|
||||||
|
因而在 Ant Design 中,我们默认将其渲染到 `body` 下,但是这又会带来新的问题。由于不在同一个容器下,当用户滚动屏幕时会发现弹出层并未跟随滚动:
|
||||||
|
|
||||||
|
![Scroll](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*d44KQqkTX90AAAAAAAAAAAAADrJ8AQ/original)
|
||||||
|
|
||||||
|
为了解决这个问题,我们提供了 `getContainer` 属性,让用户可以自定义渲染的容器。`getContainer` 方法会在组件挂载时调用,返回一个容器节点,组件会通过 `createPortal` 渲染到这个节点下。
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const PopupWrapper = () => {
|
||||||
|
const eleRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// It's much complex with timing in real world. You can view the source for more detail:
|
||||||
|
// https://github.com/react-component/portal/blob/master/src/Portal.tsx
|
||||||
|
const container: HTMLElement = getContainer(eleRef.current);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={eleRef}>
|
||||||
|
{...}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const defaultGetContainer = () => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
document.body.appendChild(div);
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SomeComponent = ({ getContainer = defaultGetContainer }) => (
|
||||||
|
<PopupWrapper getContainer={getContainer} />
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
我们暂时不关注 `getContainer` 需要动态切换挂载节点的需求(其实在过去很长时间它的确也无法切换),仅仅从 React 18 看,它遇到了一些问题。
|
||||||
|
|
||||||
|
## React 18 Concurrent Mode
|
||||||
|
|
||||||
|
React 18 中,effect 可能会多次触发。为了防止不经意间破坏开发者的行为,在 [StrictMode](https://reactjs.org/docs/strict-mode.html) 下它也做了相应的调整:
|
||||||
|
|
||||||
|
> - React mounts the component.
|
||||||
|
> - Layout effects are created.
|
||||||
|
> - Effect effects are created.
|
||||||
|
> - React simulates effects being destroyed on a mounted component.
|
||||||
|
> - Layout effects are destroyed.
|
||||||
|
> - Effects are destroyed.
|
||||||
|
> - React simulates effects being re-created on a mounted component.
|
||||||
|
> - Layout effects are created
|
||||||
|
> - Effect setup code runs
|
||||||
|
|
||||||
|
简单理解就是 StrictMode 下,即便你的 deps 里是空对象,effect 仍然会多次触发。在切换为 React 18 StrictMode 的时候,我们会发现在 HTML 中会成对出现挂载节点,同时前一个是空的:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body>
|
||||||
|
<div id="root">...</div>
|
||||||
|
|
||||||
|
<!-- Empty -->
|
||||||
|
<div className="sample-holder"></div>
|
||||||
|
|
||||||
|
<!-- Real in use -->
|
||||||
|
<div className="sample-holder">
|
||||||
|
<div className="ant-component-wrapper">...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
因而我们调整了调用实现,默认的 `getContainer` 也通过 state 进行管理,确保在 StrictMode 下会清理前一个 effect 生成的节点:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fake Code. Just for Demo
|
||||||
|
const SomeComponent = ({ getContainer }) => {
|
||||||
|
const [myContainer, setMyContainer] = React.useState<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (getContainer) {
|
||||||
|
setMyContainer(getContainer());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const div = document.createElement('div');
|
||||||
|
document.body.appendChild(div);
|
||||||
|
setMyContainer(div);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
};
|
||||||
|
}, [getContainer]);
|
||||||
|
|
||||||
|
return <PopupWrapper getContainer={() => myContainer} />;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
将 `getContainer` 放入 effect 管理后,我们可以更符合 React 生命周期的方式去管理节点,同时也可以在 `getContainer` 变化时进行清理。从而支持动态改变 `getContainer` 的场景(虽然我个人比较怀疑这种使用场景的普遍性)。
|
||||||
|
|
||||||
|
## 最后
|
||||||
|
|
||||||
|
由于修复了 `getContainer` 不支持动态改变的问题,它也引入了一个潜在的 breaking change。开发者如果自定义 `getContainer` 每次都是创建新的 dom 节点时,它就会因为 effect 不断执行,导致节点不断创建而死循环。如果你使用了这种方式并且遇到了问题,需要注意检查。
|
Loading…
Reference in New Issue
Block a user