css in js 与 css 虽然很像,但是因为是基于 js 的原因,还是有一些区别。以下会从开发和使用两个方便来进行比较。

开发组件库

Token 的 获取 和 派生

原来获取 token 是使用 less 变量或者 css 变量来管理,less 的动态性主要是靠编译工具来实现。和 css 靠的是浏览器的能力,但是无法派生。两者都有相应的问题。导致动态切主题一直有各种问题。

css in js 依托于 js 的动态性和灵活性,可以实现一些用css 变量很难实现的东西。

  1. import useStyle from './style';
  2. // 与configProvide 结合实现 组件级别的样式隔离
  3. const { getPrefixCls, autoInsertSpaceInButton, direction, iconPrefixCls } =
  4. React.useContext(ConfigContext);
  5. const prefixCls = getPrefixCls('btn', customizePrefixCls);
  6. // Style
  7. const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
  8. // 基于 token 的 派生属性
  9. const genSharedButtonStyle = (
  10. prefixCls: string,
  11. iconPrefixCls: string,
  12. token: DerivativeToken,
  13. ): CSSObject
  14. // 更方便的 hover 写法
  15. '&:hover': {
  16. display: 'inline-block',
  17. },

这样写起来会有诸多不适应,尤其是选择器方面,会让人感觉很难受需要适应一下。

选择器

css 选择器是 css 的精华,但是在 css in js 中它用起来会有点难受。以 antd 的 Button 为例。传统写法是,.ant-btn 先按照 default 按钮样式写完(这样按钮的大框就完成了)。然后再通过 css selector 减去或者改造一部分。比如 Text 和 Link 类型就需要去除边框。于是就会变成:

  1. .ant-btn {
  2. // ...
  3. &-text,
  4. &-link {
  5. border-color: transparent;
  6. backgounrd-color: transparent;
  7. }
  8. [disabled] {
  9. // ...
  10. // 也有提上到 `text` 和 `link` 的写法,看自己组织风格是什么样子的了
  11. &.ant-btn-text,
  12. &.ant-btn-link {
  13. border-color: transparent;
  14. backgounrd-color: transparent;
  15. }
  16. }
  17. }

我们会先定义一个基本样式,然后用选择器去覆盖掉它来实现不同的样式。如果样式差不多我们可以然选择器公用一段css。看起来非常精巧。

但是在 css 中这些应该都被派生出来。

image.png

实现完看起来是这样的,我们在一个基础上面不断的添加样式,css 的熟练反而增加了,但是同一时间作用的选择器反而减少了。

image.png

除了样式的派生还有一个子选择器的问题使用起来不同,在 css in js 中很少用到子选择器,尤其是根据不同的父样式来设置子样式。

  1. .@{button-prefix-cls} {
  2. &-button {
  3. bgcolor:#eee
  4. }
  5. &-primariy {
  6. &-button {
  7. bgcolor:#1890FF
  8. }
  9. }
  10. }

但是在 css in js 中这个写起来其实比较麻烦,很多库对这方面都不重视的。这里我们需要换下思路,在css in js 中我们可以这么写。

  1. const getButtonStyle=(primariy)=>{
  2. if(primariy) return { bgcolor:#1890FF}
  3. return { bgcolor:#eee }
  4. }

写多了发现这种方法更好,可以避免一些样式污染问题,同时逻辑化更清晰。

使用组件库

css in js 最大的好处就是动态能力和样式隔离,这个对于现在很难找到亮点的组件库来说让人开心,在使用的时候也会更加方便。

  1. // 默认的修改方式
  2. <Button type="primary" style={{bgColor:"red"}}>
  3. 主按钮
  4. </Button>
  5. // 新版本
  6. <ConfigProVide token={{ primaryColor:"red" }}>
  7. <Button type="primary">
  8. 主按钮
  9. </Button>
  10. </ConfigProVide>

对于复杂的组件来说,token 会一直传递下去,我们要覆盖 tabs 的边框圆角,需要记住很多 css 的 className。一旦 antd 修改就会出问题,在css in js 中我们可以这么搞。

  1. // 新版本
  2. <ConfigProVide token={{ borderRadius:"0" }}>
  3. <Button type="primary">
  4. 主按钮
  5. </Button>
  6. <Tabs>
  7. <Tabs.Panel tab="名字"/>
  8. </Tabs
  9. </ConfigProVide>

比原来好多了,随着token越来越方便,我们可以不用写 css 了。只要写 token 或者 css in js 的变量就好了。

ProLayout 的示例。

image.png

token 的配置,无需考虑优先级。

  1. token={{
  2. sider: {
  3. menuBackgroundColor: '#004FD9',
  4. menuTextColor: 'rgba(255,255,255,0.85)',
  5. menuTextColorSecondary: 'rgba(255,255,255,0.65)',
  6. menuSelectedTextColor: '#fff',
  7. menuTitleTextColor: 'rgba(255,255,255,0.95)',
  8. menuItemHoverBgColor: 'rgba(0,0,0,0.06)',
  9. menuItemCollapsedHoverBgColor: 'rgba(0,0,0,0.06)',
  10. menuItemSelectedBgColor: 'rgba(0,0,0,0.15)',
  11. menuItemCollapsedSelectedBgColor: 'rgba(0,0,0,0.15)',
  12. menuItemDividerColor: 'rgba(255,255,255,0.15)',
  13. collapsedButtonBgColor: '#fff',
  14. collapsedButtonTextColor: 'rgba(0,0,0,0.45)',
  15. collapsedButtonHoverTextColor: 'rgba(0,0,0,0.65)',
  16. menuSubArrowColor: 'rgba(255,255,255,0.15)',
  17. },
  18. appListIconTextColor: 'rgba(255,255,255,0.85)',
  19. appListIconHoverTextColor: 'rgba(255,255,255,0.95)',
  20. appListIconHoverBgColor: 'rgba(0,0,0,0.06)',
  21. }}