logoNamu Design

⌘ K
  • Design
  • Development
  • Components
  • Blog
  • Resources
1.0.0
  • 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
  • Happy Work ThemeSuspense breaks styles

    Resources

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

    Community

    Awesome Namu Design
    Medium
    Twitter
    yuqueNamu Design in YuQue
    Namu Design in Zhihu
    Experience Cloud Blog
    seeconfSEE Conf-Experience Tech Conference

    Help

    GitHub
    Change Log
    FAQ
    Bug Report
    Issues
    Discussions
    StackOverflow
    SegmentFault

    Ant XTechMore Products

    yuqueYuQue-Document Collaboration Platform
    AntVAntV-Data Visualization
    EggEgg-Enterprise Node.js Framework
    kitchenKitchen-Sketch Toolkit
    xtechAnt Financial Experience Tech
    Theme Editor
    Made with ❤ by
    Ant Group and Namu 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 Namu Design, you will find that Namu 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 Namu 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

    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:

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

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

    {
    "bAMbOo|Button": "LItTlE",
    "bAMbOo|Spin": "liGHt"
    }

    And also pack 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 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:

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

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

    // 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 Namu 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.