背景
- 项目以路由维度拆包实现按需加载脚本,避免初始页面载入暂时用不到的代码包
- 项目里引用了 arco 组件库,且按需引入样式
- 项目里同样用到了 styled-components,且经常会使用 styled-components 覆盖 arco 组件库默认样式实现设计需求
问题复现
- 一次项目迭代时,增加了个全局 loading 组件。它是使用 styled-components 书写组件样式
- 导致别的页面一个 button 样式出现问题。具体变现从图1 变成了 图2,styled-components 覆盖的样式失效
问题分析
组件库按需加载样式
由于设置了组件库样式按需加载,导致只有在加载相关页面时才会加载页面需要的 arco 组件的样式
styled-components 运行机制
-
styled-components 是 css in js的一种实现方式。本身是一个运行时库会被打
-
当styled-components包裹的组件被解析执行的时候,styled-components 会动态生成一个唯一的CSS类,并把组件包裹的CSS样式通过style标签的形式插入到html里
- 虽然 styled-components 会根据组件加载情况动态添加 CSS 样式,但是增量的 CSS 样式也是添加在已存在style标签内
css样式规则
根据css样式优先级计算,都是单个类名权重,后加载的文件样式会覆盖前面的文件样式
代码层面改动
增加了一个全局loading组件(styled-components写的样式),使生成 style 标签插入的时机提前了
导致,之前styled-components 覆盖 arco 组件的样式部分失效
总结
尽量保证使用 styled-components 时其生成的 sytle 标签的插入时机是不变的
- 不考虑组件库样式按需加载
如果组件库样式会全量加载,一般会在打包文件入口处引入,由于 styled-components 包裹的组件样式是按需插入的,因此 styled-components 生成 sytle 标签的插入时机是稳定落后 组件库样式的
- 考虑组件库样式按需加载
尽量保证 styled-components 插入机制在前(没法保证所有SC组件都在第三方组件库后)。
如果遇到上述情况 styled-components 样式被 arco组件库样式覆盖,对于需要覆盖组件库样式的地方增加权重
styled-components 优缺点对比
优点
局部作用域
css没有本地作用域,所有声明的样式都是全局的(global styles)。页面上任意元素只要匹配上某个选择器的规则,这个规则就会被应用上,而且规则和规则之间可以叠加作用
styled-components 动态生成的CSS选择器会有一小段哈希值来保证全局唯一性,来避免样式发生冲突。
消除人肉在 dom 和 css 之间做映射
不用 CSS, JSX 之间跳来跳去,一个组件搞定
Critical CSS
浏览器在将我们的页面呈现给用户之前一定要先完成页面引用到的CSS文件的下载和解析。社区有一种优化方案就是将一些重要的CSS代码(Critical CSS)直接放在头部的style标签内,其余的CSS代码再进行异步加载,这样浏览器在解析完HTML后就可以直接渲染页面
styled-components 对Critical CSS是自动支持的。可以动态追踪组件状态插入 styles,结合代码拆分,可以实现加载最少量的代码
基于JS的灵活性
样式可以使用变量,更加灵活。且不依赖各种css预处理器
避免无用css样式堆积
在开发新的功能或者进行代码重构的时候,由于HTML代码和CSS样式之间没有显式的一一对应关系,我们很难辨认出项目中哪些CSS样式代码是有用的、哪些是无用的,这就导致了我们不敢轻易删除代码中可能是无用的样式
适用于 react-native 这类本身就没有 css 的运行环境
缺点
运行时消耗
打包出来的JS代码,包含 styled-components 运行时代码大小是12.42kB min + gzip
代码运行过程中,不断动态建立 Element 和 建立 style 会有一定性能代价。
与全局 CSS 共存时,样式可能不符合预期
参考 上文分析
抽取组件的扩展性问题
styled-components 生成的 className 通常是不稳定的随机串。对于业务里抽取的通用组件,别人使用时很难从外部灵活覆盖样式。
使用 styled-components 提取业务通用业务组件时候,需要更多考虑组件样式扩展性问题
参考
- CSS in JS的好与坏
- 一些 CSS 管理方案的优缺点 | Unknown Me
- 如何评价 styled-components? - 知乎
- styled-components.com/docs/advanc…
- styled-components不能覆盖antd组件样式?
扩展
组件库 按需加载 的现状
重新 Review 大包组件 js 按需引入&样式自动且按需引入方案 · Issue #4703 · alibaba/ice
按需加载的组件库,内部引用的组件库没有按需加载 · Issue #369 · ant-design/babel-plugin-import
根据文档使用babel-plugin-import按需加载antd,但是npm run build 之后的包依然有600kb · Issue #16600 · ant-design/ant-desig