css in js 与 css 虽然很像,但是因为是基于 js 的原因,还是有一些区别。以下会从开发和使用两个方便来进行比较。
开发组件库
Token 的 获取 和 派生
原来获取 token 是使用 less 变量或者 css 变量来管理,less 的动态性主要是靠编译工具来实现。和 css 靠的是浏览器的能力,但是无法派生。两者都有相应的问题。导致动态切主题一直有各种问题。
css in js 依托于 js 的动态性和灵活性,可以实现一些用css 变量很难实现的东西。
import useStyle from './style';
// 与configProvide 结合实现 组件级别的样式隔离
const { getPrefixCls, autoInsertSpaceInButton, direction, iconPrefixCls } =
React.useContext(ConfigContext);
const prefixCls = getPrefixCls('btn', customizePrefixCls);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls, iconPrefixCls);
// 基于 token 的 派生属性
const genSharedButtonStyle = (
prefixCls: string,
iconPrefixCls: string,
token: DerivativeToken,
): CSSObject
// 更方便的 hover 写法
'&:hover': {
display: 'inline-block',
},
这样写起来会有诸多不适应,尤其是选择器方面,会让人感觉很难受需要适应一下。
选择器
css 选择器是 css 的精华,但是在 css in js 中它用起来会有点难受。以 antd 的 Button 为例。传统写法是,.ant-btn
先按照 default 按钮样式写完(这样按钮的大框就完成了)。然后再通过 css selector 减去或者改造一部分。比如 Text 和 Link 类型就需要去除边框。于是就会变成:
.ant-btn {
// ...
&-text,
&-link {
border-color: transparent;
backgounrd-color: transparent;
}
[disabled] {
// ...
// 也有提上到 `text` 和 `link` 的写法,看自己组织风格是什么样子的了
&.ant-btn-text,
&.ant-btn-link {
border-color: transparent;
backgounrd-color: transparent;
}
}
}
我们会先定义一个基本样式,然后用选择器去覆盖掉它来实现不同的样式。如果样式差不多我们可以然选择器公用一段css。看起来非常精巧。
但是在 css 中这些应该都被派生出来。
实现完看起来是这样的,我们在一个基础上面不断的添加样式,css 的熟练反而增加了,但是同一时间作用的选择器反而减少了。
除了样式的派生还有一个子选择器的问题使用起来不同,在 css in js 中很少用到子选择器,尤其是根据不同的父样式来设置子样式。
.@{button-prefix-cls} {
&-button {
bgcolor:#eee
}
&-primariy {
&-button {
bgcolor:#1890FF
}
}
}
但是在 css in js 中这个写起来其实比较麻烦,很多库对这方面都不重视的。这里我们需要换下思路,在css in js 中我们可以这么写。
const getButtonStyle=(primariy)=>{
if(primariy) return { bgcolor:#1890FF}
return { bgcolor:#eee }
}
写多了发现这种方法更好,可以避免一些样式污染问题,同时逻辑化更清晰。
使用组件库
css in js 最大的好处就是动态能力和样式隔离,这个对于现在很难找到亮点的组件库来说让人开心,在使用的时候也会更加方便。
// 默认的修改方式
<Button type="primary" style={{bgColor:"red"}}>
主按钮
</Button>
// 新版本
<ConfigProVide token={{ primaryColor:"red" }}>
<Button type="primary">
主按钮
</Button>
</ConfigProVide>
对于复杂的组件来说,token 会一直传递下去,我们要覆盖 tabs 的边框圆角,需要记住很多 css 的 className。一旦 antd 修改就会出问题,在css in js 中我们可以这么搞。
// 新版本
<ConfigProVide token={{ borderRadius:"0" }}>
<Button type="primary">
主按钮
</Button>
<Tabs>
<Tabs.Panel tab="名字"/>
</Tabs
</ConfigProVide>
比原来好多了,随着token越来越方便,我们可以不用写 css 了。只要写 token 或者 css in js 的变量就好了。
ProLayout 的示例。
token 的配置,无需考虑优先级。
token={{
sider: {
menuBackgroundColor: '#004FD9',
menuTextColor: 'rgba(255,255,255,0.85)',
menuTextColorSecondary: 'rgba(255,255,255,0.65)',
menuSelectedTextColor: '#fff',
menuTitleTextColor: 'rgba(255,255,255,0.95)',
menuItemHoverBgColor: 'rgba(0,0,0,0.06)',
menuItemCollapsedHoverBgColor: 'rgba(0,0,0,0.06)',
menuItemSelectedBgColor: 'rgba(0,0,0,0.15)',
menuItemCollapsedSelectedBgColor: 'rgba(0,0,0,0.15)',
menuItemDividerColor: 'rgba(255,255,255,0.15)',
collapsedButtonBgColor: '#fff',
collapsedButtonTextColor: 'rgba(0,0,0,0.45)',
collapsedButtonHoverTextColor: 'rgba(0,0,0,0.65)',
menuSubArrowColor: 'rgba(255,255,255,0.15)',
},
appListIconTextColor: 'rgba(255,255,255,0.85)',
appListIconHoverTextColor: 'rgba(255,255,255,0.95)',
appListIconHoverBgColor: 'rgba(0,0,0,0.06)',
}}