什么是 CSS-in-JS
像 js 一样地使用 css 。表现有2种:
css写法维持不变,但js可引用,可调用(css Module);- 使用
js来书写css,更灵活(JSS/styled-components)。
为什么要用 CSS-in-JS
为什么要用 CSS-in-JS ?大概是它可以解决 CSS 的一些痛点和带来了一些明显的爽点吧。
-
作用范围
一般来说,样式的作用范围是全局的,但我们是模块化,组件化开发,希望是样式在当前模块/组件范围内有效,而不会影响到其他模块/组件/全局的样式。
Vue中使用scoped来让当前组件的样式不会影响到其他范围。平时我们也可以使用一个类选择器/ID选择器将当前的样式包裹在内,使样式有所局限。
-
潜在依赖
同一个元素,可能叠加了多个样式规则。一个样式规则的定义,可能包含着多个元素的样式规则。
What actually is CSS-in-JS?
-
A CSS rule includes multiple selectors to target different HTML elements.
-
Multiple class names or other attributes are applied to HTML elements, causing them to be targeted by multiple CSS rules.
-
-
无用代码
css 可复用,但在复用过程中,某些样式可能并不是目标元素想要的/有效果的。此时,这些多余的/无效的代码,就是无用代码。
-
加载顺序造成影响
页面A和页面B 都有一个相同的类选择器,但是样式设置是不一样的。先进入页面A,cssA文件下载了,作用了,然后进入页面B,cssB 文件下载了,也作用了。此时,页面是正常的。但是返回页面A的时候,发现页面A的样式出现了问题--因为此时 cssB 里定义的样式覆盖了 cssA 里定义的了。这个就是加载顺序造成的样式影响。
-
1对多的依赖
同一个样式规则,可能应用在多个元素上。如果想改动这个样式规则,代价就非常大了。
-
可维护性更高
样式规则定义的颗粒度小(元素),作用范围小(组件),且使用
js来编写/引入。层级关系比css的写法更明显。 -
灵活性更高
使用
js编写,可引入变量,使用方法,根据当时环境来执行,灵活度可想而知。
以上,都可能是你想要用 CSS-in-JS 的原因。总结一下,就是:使样式规则定义颗粒度更细更专一;提倡组件复用(即元素+样式)而不是样式复用;更好的可维护性和灵活性。
怎么使用
不同的库,使用方法不一致。如:
css Module:
仍需要 .css 文件,且 CSS-in-JS 的味道很淡。
// 编译前
// app.css
.txt {
color: '#f00';
}
// app.js
import React from 'react';
import style from './app.css';
export default () => (
<p className={style.txt}>hello</p>
)
// 编译工具编译后
<h1 class="_3zyde4l1yATCOkgn-DBWEL">
Hello World
</h1>
// app.css
._3zyde4l1yATCOkgn-DBWEL {
color: red;
}
jss:
干掉了 .css 文件,使用 JSON对象 来表示。可抽离,但抽离后,就仿 佛是 .css 转变为 .js 。
// 代码来自 https://cssinjs.org/?v=v10.5.0
import React from 'react'
import {render} from 'react-dom'
import {createUseStyles} from 'react-jss'
const useStyles = createUseStyles({
myButton: {
color: 'green',
margin: {
// jss-expand gives more readable syntax
top: 5, // jss-default-unit makes this 5px
right: 0,
bottom: 0,
left: '1rem'
},
'& span': {
// jss-nested applies this to a child span
fontWeight: 'bold' // jss-camel-case turns this into 'font-weight'
}
},
myLabel: {
fontStyle: 'italic'
}
})
const Button = ({children}) => {
const classes = useStyles()
return (
<button className={classes.myButton}>
<span className={classes.myLabel}>{children}</span>
</button>
)
}
const App = () => <Button>Submit</Button>
render(<App />, document.getElementById('root'))
styled-components :
最受欢迎的 CSS-in-JS 库。使用方法别具一格。
const Button = styled.button`
color: '#f00'
`;
function Temp() {
return <Button />;
}
styled-components 使用方法实现原理
先看下 styled-components 的简单的使用:
const Button = styled.button`
color: '#f00'
`;
这里的诀窍就是:hello('world') == hello`world` (只是调用效果类似,并不全等)。
所以
const Button = styled.button`
color: '#f00'
`;
// 等同于
const Button = styled.button(["color: '#f00'", ["color: '#f00'"]]);
为什么 hello('world') == hello`world` 呢?
从 AST 上看,**hello`world** 也是一个表达式(TaggedTemplateExpression`)来的。所以无怪这样怪异的语句也可以执行了。
当然, styled-components 的使用方法实现原理还有很多其他的处理,比如:
const Button = styled.button`
color: {props => props.color ? props.color : '#f00'}
`;
我们现在只是弄懂了:为什么 styled-components 可以使用类似 styled.button`...` 这样的调用方式来调用而已。
还有很多细节可以继续了解。
参考
- JSS cssinjs.org/?v=v10.5.0
- styled-components styled-components.com/docs/basics
- TaggedTemplateExpression github.com/estree/estr…
- astexplorer.net/
- What actually is CSS-in-JS? medium.com/dailyjs/wha…