mirror of
https://github.com/ant-design/ant-design.git
synced 2024-12-13 23:59:12 +08:00
123 lines
5.8 KiB
Markdown
123 lines
5.8 KiB
Markdown
|
---
|
||
|
title: Where is the dynamic style?
|
||
|
date: 2023-07-21
|
||
|
author: zombieJ
|
||
|
---
|
||
|
|
||
|
As we all know, antd v5 uses CSS-in-JS to support the needs of mixed and dynamic styles. On the contrary, it needs to generate styles at runtime, which will cause some performance loss. Therefore, we developed the `@ant-design/cssinjs` library at the component library level to improve the cache efficiency through certain constraints, so as to achieve the purpose of performance optimization. But we don't stop there. We can skip the stage of generating styles at runtime through some logic.
|
||
|
|
||
|
## Where is the dynamic style?
|
||
|
|
||
|
If you have checked the official website of Ant Design, you will find that Ant Design's components do not dynamically insert `<style />` to control styles, but use CSS files to control styles:
|
||
|
|
||
|
- <img width="376" alt="button" src="https://github.com/ant-design/ant-design/assets/5378891/82fc5e7a-8d68-4c37-b892-e75097f80ff8" />
|
||
|
- <img width="480" alt="style" src="https://github.com/ant-design/ant-design/assets/5378891/ab31820e-6602-4421-9101-50cb70738058" />
|
||
|
|
||
|
`document.head` has some `css` file references:
|
||
|
|
||
|
- umi.\[hash].css
|
||
|
- style-acss.\[hash].css
|
||
|
|
||
|
The former is the style content generated by dumi, such as Demo block, search box style, etc. The latter is the style file generated by SSR. In the [custom theme](/docs/react/customize-theme) document, we mentioned that we can pre-bake the components used in the page through the overall export method, so as to generate css files for cache hit to improve the next open speed. This is also the way we use in the official website. So the components in the Demo actually reuse this part of the style.
|
||
|
|
||
|
Wait a minute! Isn't CSS-in-JS supposed to generate style hash at runtime and align it with `<style />`? Why can css files also be aligned? Don't worry, let's take a look.
|
||
|
|
||
|
## CSS-in-JS Hydration
|
||
|
|
||
|
Application level CSS-in-JS solutions will calculate the hash value of the generated style and store it in the cache. When rerender, it will first check whether the corresponding style exists in the cache. If it exists, it will be used directly, otherwise it will be generated again. This can avoid repeated generation of styles, so as to improve performance.
|
||
|
|
||
|
![CSS-in-JS process](https://github.com/ant-design/ant-design/assets/5378891/aa8825c9-a78a-4326-ac13-30a27cbe14b6)
|
||
|
|
||
|
Every dynamically inserted style is also identified by hash. If the `<style />` with the hash already exists in the page, it means that inline style injection has been done in SSR. Then `<style />` does not need to be created again.
|
||
|
|
||
|
You can find that although the `<style />` node can be omitted, hash still deps on the calculated style content. So even if there is reusable style in the page, it still needs to be calculated once. It's really not cost-effective.
|
||
|
|
||
|
## Component-level CSS-in-JS
|
||
|
|
||
|
In the [component-level CSS-in-JS](/docs/blog/css-in-js) article, we mentioned that Ant Design's Cache mechanism does not need to calculate the complete style. For the component library, as long as the Token and ComponentName can determine the consistency of the generated style, so we can calculate the hash value in advance:
|
||
|
|
||
|
![Component CSS-in-JS](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*yZMNSYVtxnAAAAAAAAAAAAAADrJ8AQ/original)
|
||
|
|
||
|
Therefore, we found that we can reuse this mechanism to realize whether the component style has been injected on the client side.
|
||
|
|
||
|
## SSR HashMap
|
||
|
|
||
|
In `@ant-design/cssinjs`, Cache itself contains the style and hash information corresponding to each element. The previous `extractStyle` method only takes the content of style in Cache for packaging:
|
||
|
|
||
|
```tsx
|
||
|
// e.g. Real world path is much more complex
|
||
|
{
|
||
|
"bAMbOo|Button": ["LItTlE", ":where(.bAMbOo).ant-btn { color: red }"],
|
||
|
"bAMbOo|Spin": ["liGHt", ":where(.bAMbOo).ant-spin { color: blue }"]
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Get:
|
||
|
|
||
|
```css
|
||
|
:where(.bAMbOo).ant-btn {
|
||
|
color: red;
|
||
|
}
|
||
|
:where(.bAMbOo).ant-spin {
|
||
|
color: blue;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
We go further to reuse the style. We also extract the path and hash value:
|
||
|
|
||
|
```tsx
|
||
|
{
|
||
|
"bAMbOo|Button": "LItTlE",
|
||
|
"bAMbOo|Spin": "liGHt"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And also pack into css style:
|
||
|
|
||
|
```less
|
||
|
// Just example. Not real world code
|
||
|
.cssinjs-cache-path {
|
||
|
content: 'bAMbOo|Button:LItTlE;bAMbOo|Spin:liGHt';
|
||
|
}
|
||
|
```
|
||
|
|
||
|
In this way, the SSR side will retain all the information we need, and then we only need to extract it on the client side.
|
||
|
|
||
|
## CSR HashMap
|
||
|
|
||
|
It's much simpler on the client side. We can extract the HashMap information through `getComputedStyle`:
|
||
|
|
||
|
```tsx
|
||
|
// Just example. Not real world code
|
||
|
const measure = document.createElement('div');
|
||
|
measure.className = 'cssinjs-cache-path';
|
||
|
document.body.appendChild(measure);
|
||
|
|
||
|
// Now let's parse the `content`
|
||
|
const { content } = getComputedStyle(measure);
|
||
|
```
|
||
|
|
||
|
In the component rendering stage, `useStyleRegister` will first check whether the path exists in HashMap before calculating CSS Object. If it exists, it means that the data has been generated through the server. We only need to extract the style from the existing `<style />`:
|
||
|
|
||
|
```tsx
|
||
|
// e.g. Real world path is much more complex
|
||
|
{
|
||
|
"bAMbOo|Button": ["LItTlE", "READ_FROM_INLINE_STYLE"],
|
||
|
"bAMbOo|Spin": ["liGHt", "READ_FROM_INLINE_STYLE"]
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And for the style provided by CSS file (such as the usage of the official website), it will not be removed like `<style />`, we can directly mark it as from CSS file. Like inline style, they will be skipped in the `useInsertionEffect` stage.
|
||
|
|
||
|
```tsx
|
||
|
// e.g. Real world path is much more complex
|
||
|
{
|
||
|
"bAMbOo|Button": ["LItTlE", "__FROM_CSS_FILE__"],
|
||
|
"bAMbOo|Spin": ["liGHt", "__FROM_CSS_FILE__"]
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Summary
|
||
|
|
||
|
CSS-in-JS has been criticized for its runtime performance loss. In Ant Design, if your application uses SSR, you can skip the stage of generating styles at runtime on the client side to improve performance. Of course, we will continue to follow up the development of CSS-in-JS to bring you a better experience.
|