docs: Update Blog (#39399)

* docs: auto order

* docs: getContainer blog

* chore: rm console
This commit is contained in:
二货爱吃白萝卜 2022-12-08 16:46:48 +08:00 committed by GitHub
parent b2df64a057
commit 33deda5e38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 267 additions and 8 deletions

View File

@ -115,8 +115,14 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
});
}
} 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(
...(group.children?.map((item) => ({
...list.map((item) => ({
label: (
<Link to={`${item.link}${search}`}>
{before}
@ -125,7 +131,7 @@ const useMenu = (options: UseMenuOptions = {}): [MenuProps['items'], string] =>
</Link>
),
key: item.link.replace(/(-cn$)/g, ''),
})) ?? []),
})),
);
}
return result;

View File

@ -1,8 +1,10 @@
import type { ReactNode } from 'react';
import React, { useMemo, useState, useLayoutEffect, useContext } from 'react';
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 DayJS from 'dayjs';
import { css } from '@emotion/react';
import classNames from 'classnames';
import Footer from '../Footer';
@ -183,6 +185,25 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
/>
)}
</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}
{meta.frontmatter.filename && (
<ContributorsList

View File

@ -206,7 +206,13 @@ export default ({
blogList.length
? {
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}
</Link>
),

View File

@ -1,6 +1,7 @@
---
order: 0
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>

View File

@ -1,10 +1,9 @@
---
order: 0
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 方案。
## CSS-in-JS 的困境

View 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 dont 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.

View 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 不断执行,导致节点不断创建而死循环。如果你使用了这种方式并且遇到了问题,需要注意检查。