Where did the dynamic styles go?

The author of this article is Doujiang, a front-end engineer of Ant Group. As we all know, antd v5 uses CSS-in-JS technology to support the needs of mixed and dynamic styles. On the contrary, it needs to generate styles at runtime, which will cause a certain performance loss. Therefore, we have 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 directly skip the stage of generating styles at runtime through some logic.

Where did the dynamic styles go?

If you have studied Ant Design's official website, you will find that Ant Design's components are not dynamically inserted to control styles, but are controlled through CSS files:

image.png

image.png

There are several css file references in document.head:

  • umi.[hash].css
  • style-acss.[hash].css

The former is the style content generated by dumi, such as Demo block, search box style and so on. The latter is the style file generated by SSR. In the custom theme documentation, we mentioned that the components used in the page can be pre-baked through the overall export method, so as to generate css files for cache hits to improve the next opening speed. This is also the way we use it on our official website. So the components in the Demo actually reuse this part of the style. etc! Doesn't CSS-in-JS need to generate a hash of the style at runtime and then align it? Why css files can also be aligned? Don't worry, let's take our time.

CSS-in-JS water injection

The application-level CSS-in-JS solution calculates the hash value for the generated style and stores it in the Cache. When rendering next time, it will first check whether there is a corresponding style from the Cache. If it exists, it will be used directly, otherwise it will be generated again. This avoids repeated generation of styles, which improves performance.

image.png

Each style dynamically inserted into the page also uses hash as a unique identifier. If the hash already exists on the page, it means that inline style injection has been done in SSR. Then there is no need to create it again. You can find that although the node creation can be omitted, but because the hash depends on the calculated style content. So even if there is already reusable style content in the page, it still needs to be calculated once. It's not worth it.

Component-Level CSS-in-JS

In the CSS-in-JS article at the component level , we mentioned it. The Cache mechanism of Ant Design does not need to calculate the complete style. For the component library, as long as the generated style consistency can be determined through Token and ComponentName, we can calculate the hash value in advance: therefore , we found that this mechanism can be reused to realize whether the component style is perceived on the client side. already injected.

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 the style in the Cache for encapsulation:

// 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 }"]

}

extract:

:where(.bAMbOo).ant-btn {

  color: red;

}

:where(.bAMbOo).ant-spin {

  color: blue;

}

To reuse styles, we go a step further. The path and hash values ​​are also extracted:

{

  "bAMbOo|Button": "LItTlE",

  "bAMbOo|Spin""liGHt"

}

And also typed into css style:

// Just example. Not real world code

.cssinjs-cache-path {

  content'bAMbOo|Button:LItTlE;bAMbOo|Spin:liGHt';

}

In this way, the SSR side saves all the information we need, and then only needs to extract it on the client side.

CSR HashMap

On the client side, it is much simpler. We can extract the HashMap information through getComputedStyle and save it:

// 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 phase, before usingStyleRegister calculates the CSS Object, it will first check whether the path exists in the HashMap. If it exists, it means that the data has been generated by the server. We just need to extract the styles from the existing ones:

// e.g. Real world path is much more complex

{

  "bAMbOo|Button": ["LItTlE""READ_FROM_INLINE_STYLE"],

  "bAMbOo|Spin": ["liGHt""READ_FROM_INLINE_STYLE"]

}

As for the style provided by the CSS file (such as the use of the official website), it does not seem to be removed, we can directly mark it as coming from the CSS file. Like the inline style, they are skipped during the useInsertionEffect phase.

// e.g. Real world path is much more complex

{

  "bAMbOo|Button": ["LItTlE""__FROM_CSS_FILE__"],

  "bAMbOo|Spin": ["liGHt""__FROM_CSS_FILE__"]

}

Summarize

CSS-in-JS has been criticized for its runtime performance penalty. In Ant Design, if your application uses SSR, you can directly 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.

Guess you like

Origin juejin.im/post/7265249262329839672