logoAnt Design

⌘ K
  • Design
  • Development
  • Components
  • Blog
  • Resources
5.25.4
  • CSS in v6
  • 👀 Visual Regression Testing
  • Why is it so hard to disable the date?
  • HOC Aggregate FieldItem
  • Line Ellipsis Calculation
  • 📢 v4 surpassed maintenance period
  • Type Util
  • A build ghost
  • Ant Design meets CSS Variables
  • Historical Debt of API
  • Stacked Notification
  • Color Models and Color Picker
  • Extends Theme
  • Virtual Table is here!
  • Happy Work Theme
  • Where is the dynamic style?
  • Suspense breaks styles
  • Bundle Size Optimization
  • Hi, GitHub Actions
  • To be what you see
  • Pain of static methods
  • SSR Static style export
  • Dependency troubleshooting
  • Contributor development maintenance guide
  • Repost: How to submit a riddle
  • Tooltip align update
  • Unnecessary Rerender
  • How to Grow as a Collaborator
  • Funny Modal hook BUG
  • about antd test library migration
  • Tree's check conduction
  • Some change on getContainer
  • Component-level CSS-in-JS
Where is the dynamic style?
CSS-in-JS Hydration
Component-level CSS-in-JS
SSR HashMap
CSR HashMap
Summary

Where is the dynamic style?

2023-07-21
@zombieJ
contributors
  • Happy Work ThemeSuspense breaks styles

    Resources

    Ant Design X
    Ant Design Charts
    Ant Design Pro
    Pro Components
    Ant Design Mobile
    Ant Design Mini
    Ant Design Web3
    Ant Design Landing-Landing Templates
    Scaffolds-Scaffold Market
    Umi-React Application Framework
    dumi-Component doc generator
    qiankun-Micro-Frontends Framework
    Ant Motion-Motion Solution
    China Mirror 🇨🇳

    Community

    Awesome Ant Design
    Medium
    Twitter
    yuque logoAnt Design in YuQue
    Ant Design in Zhihu
    Experience Cloud Blog
    seeconf logoSEE Conf-Experience Tech Conference

    Help

    GitHub
    Change Log
    FAQ
    Bug Report
    Issues
    Discussions
    StackOverflow
    SegmentFault

    Ant XTech logoMore Products

    yuque logoYuQue-Document Collaboration Platform
    AntV logoAntV-Data Visualization
    Egg logoEgg-Enterprise Node.js Framework
    Kitchen logoKitchen-Sketch Toolkit
    Galacean logoGalacean-Interactive Graphics Solution
    xtech logoAnt Financial Experience Tech
    Theme Editor
    Made with ❤ by
    Ant Group and Ant Design Community

    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:

    • button
    • style

    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 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

    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 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:

    CSS-in-JS hash compute way

    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.