记一次 组件库样式按需引入 与 styled components 导致的Bug排查分析

852 阅读5分钟

背景

  1. 项目以路由维度拆包实现按需加载脚本,避免初始页面载入暂时用不到的代码包
  2. 项目里引用了 arco 组件库,且按需引入样式
  3. 项目里同样用到了 styled-components,且经常会使用 styled-components 覆盖 arco 组件库默认样式实现设计需求

问题复现

  1. 一次项目迭代时,增加了个全局 loading 组件。它是使用 styled-components 书写组件样式
  2. 导致别的页面一个 button 样式出现问题。具体变现从图1 变成了 图2,styled-components 覆盖的样式失效

image.png

image.png

问题分析

组件库按需加载样式

由于设置了组件库样式按需加载,导致只有在加载相关页面时才会加载页面需要的 arco 组件的样式

styled-components 运行机制

  1. styled-components 是 css in js的一种实现方式。本身是一个运行时库会被打

  2. 当styled-components包裹的组件被解析执行的时候,styled-components 会动态生成一个唯一的CSS类,并把组件包裹的CSS样式通过style标签的形式插入到html里

image.png

  1. 虽然 styled-components 会根据组件加载情况动态添加 CSS 样式,但是增量的 CSS 样式也是添加在已存在style标签内

p__.gif

css样式规则

根据css样式优先级计算,都是单个类名权重,后加载的文件样式会覆盖前面的文件样式

image.png

代码层面改动

增加了一个全局loading组件(styled-components写的样式),使生成 style 标签插入的时机提前了

导致,之前styled-components 覆盖 arco 组件的样式部分失效

总结

尽量保证使用 styled-components 时其生成的 sytle 标签的插入时机是不变的

  1. 不考虑组件库样式按需加载

如果组件库样式会全量加载,一般会在打包文件入口处引入,由于 styled-components 包裹的组件样式是按需插入的,因此 styled-components 生成 sytle 标签的插入时机是稳定落后 组件库样式的

  1. 考虑组件库样式按需加载

尽量保证 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 提取业务通用业务组件时候,需要更多考虑组件样式扩展性问题

参考

扩展

组件库 按需加载 的现状

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