--- title: SSR 静态样式导出 date: 2023-04-25 author: zombieJ zhihu_url: https://zhuanlan.zhihu.com/p/639266657 yuque_url: https://www.yuque.com/ant-design/ant-design/gyacdbtixle9bbm4 juejin_url: https://juejin.cn/post/7322352551088603163 --- 传统的 js + css 网站,SSR 一般只需要处理好首次渲染的注水问题。而当 CSS-in-JS 技术的引入,开发者则需要额外关注如何将样式导出到 HTML 中,以保证首次渲染的正确性。我们提供了非常多的实现方式,也正好在此聊聊其中的思路。如果你需要完整的文档或者示例欢迎查阅[《定制主题》](/docs/react/customize-theme-cn)。 ### Inline Style > 最简单的方式就是将样式直接内联到 HTML 中,这样就不需要额外的请求。这种方式的缺点是,样式无法被浏览器缓存,每次请求都需要重新下载。而且,如果样式过多,会导致 HTML 文件过大,影响首次渲染的速度。 在 v5 alpha 版本中,为了兜底 SSR 样式渲染,我们参考 `Emotion` 的实现,为每个元素前都加上对应的内联样式: ```html
``` 这个实现简单有效,唯一的缺点则是对于 `:nth` 选择会有样式污染。但是考虑到 antd 组件其实很少使用这个样式,副作用对我们没什么影响。 开始的时候运行良好,antd 的官网几乎无需改造就直接支持 SSR 样式满足了 SEO 需求。但是随着我们组件逐渐迁移到 CSS-in-JS 版本后,我们发现站点的构建产物变得十分巨大,慢慢的变得不可用。在查看 HTML 后,我们发现默认内联方式并不好,它会导致样式被成倍的内联,例如一个页面里有 3 个 Button,那它就会重复内联 3 次: ```html
``` 而当大部分组件都转成 CSS-in-JS 后,内联样式会变得十分巨大。所以我们在后期移除了自动内联的功能,转成了需要手工收取的形式: ```tsx import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs'; import { renderToString } from 'react-dom/server'; const cache = createCache(); // HTML Content const html = renderToString( , ); // Style Content const styleText = extractStyle(cache); ``` 这是传统的 CSS-in-JS 注入实现。就如引言所述,内联样式无法缓存会造成额外的加载开销。也因此,我们尝试探索一些新的实现方式,可以获得如原生 CSS 的加载体验。 ### Static Extract Style 我们在思考是否可以如 v4 版本一样,预先烘焙组件的样式来使前端消费,所以提出了 [\[RFC\] Static Extract style](https://github.com/ant-design/ant-design/discussions/40985)。它的思路很简单,我们只需要提前将所有的组件进行一次渲染就可以从 cache 中获得完整的样式,然后将其写入到 css 文件中即可。 ```tsx const cache = createCache(); // HTML Content renderToString(