CSS-in-JS
CSS-in-JS是一种样式方法,它将CSS模型抽象到组件级别,而不是文档级别。 这个想法是CSS可以限定为特定组件 - 并且只限于该组件 - 以使这些特定样式不与其他组件共享或泄露到其他组件,并且仅在需要时才调用。 CSS-in-JS库通过在中插入
使用这个概念的第一个库是JSS。 以下是使用其语法的示例:
import React from 'react'
import injectSheet from 'react-jss'
import { getSrc, getSrcSet } from './utils'
const styles = {
photo: {
width: 200,
'@media (min-width: 30rem)': {
width: 400,
},
borderRadius: props => (props.rounded ? '1rem' : 0),
},
}
const Photo = ({ classes, publicId, alt }) => (
<figure>
<img
className={classes.photo}
src={getSrc({ publicId, width: 200 })}
srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}
sizes="(min-width: 30rem) 400px, 200px"
/>
<figcaption>{alt}</figcaption>
</figure>
)
Photo.defaultProps = {
rounded: false,
}
export default injectSheet(styles)(Photo)
乍一看,样式对象看起来像用对象表示法编写的CSS,带有附加功能,比如传递一个函数来设置基于props的值。 生成的类是唯一的,因此您永远不必担心它们与其他样式冲突。 换句话说,你可以自由的使用作用域! 这就是大多数CSS-in-JS库的工作方式 - 当然,我们将在功能和语法方面进行一些改进。
您可以通过属性看到渲染图像的宽度从200px开始,然后当视口宽度变为至少30rem时,宽度增加到400px宽。 我们生成了额外的800宽度,以覆盖更大的屏幕密度:
1x screens 使用 200 and 400
2x screens 使用 400 and 800
styled-components是另一个CSS-in-JS库,但是使用更熟悉的语法巧妙地使用模板文字而不是对象看起来更像CSS:
import React from 'react'
import styled, { css } from 'styled-components'
import { getSrc, getSrcSet } from './utils'
const mediaQuery = '(min-width: 30rem)'
const roundedStyle = css`
border-radius: 1rem;
`
const Image = styled.img`
width: 200px;
@media ${mediaQuery} {
width: 400px;
}
${props => props.rounded && roundedStyle};
`
const Photo = ({ publicId, alt, rounded }) => (
<figure>
<Image
src={getSrc({ publicId, width: 200 })}
srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}
sizes={`${mediaQuery} 400px, 200px`}
rounded={rounded}
/>
<figcaption>{alt}</figcaption>
</figure>
)
Photo.defaultProps = {
rounded: false,
}
export default Photo
我们经常创建语义中性元素,如
我最喜欢这种语法的好处是它就像常规的CSS,减去插值。这意味着我们可以更轻松地迁移CSS代码,并且我们可以使用现有的css知识,而不必熟悉在对象语法中编写CSS。
请注意,我们可以在我们的样式中插入几乎任何东西。此特定示例演示了如何将媒体查询保存在变量中并在多个位置重用它。响应式图像是一个很好的用例,因为sizes属性基本上包含CSS,所以我们可以使用JavaScript来使代码更简洁。
假设我们决定在视觉上隐藏字幕,但仍然可以让屏幕阅读器访问它。我知道实现这一目标的更好方法是使用alt属性,但为了这个例子,让我们使用不同的方式。我们可以使用一个名为polished的样式mixin库 - 它适用于CSS-in-JS库,非常适合我们的示例。这个库包含一个名为hideVisually的mixin,它正是我们想要的,我们可以通过插入它的返回值来使用它:
import { hideVisually } from ‘polished’
const Caption = styled.figcaption${hideVisually()};
CSS-in-JS库具有许多高级功能,如主题,供应商前缀甚至内联关键CSS,这使得完全停止编写CSS文件变得容易。 此时,您可以开始了解为什么CSS-in-JS成为一个诱人的概念。
缺点和局限
CSS-in-JS的明显缺点是它引入了一个运行时:需要通过JavaScript加载,解析和执行样式。 CSS-in-JS库的作者正在添加各种智能优化,如Babel插件,但仍然存在一些运行时成本。
同样重要的是要注意PostCSS没有解析这些库,因为PostCSS不是设计用于运行时的。许多人使用stylis作为结果,因为它更快。这意味着我们遗憾的是无法使用PostCSS插件。
我要提到的最后一个缺点是工具。 CSS-in-JS正在以非常快的速度发展,文本编辑器扩展,linters,代码格式化等等需要追赶新功能以保持同等水平。例如,人们正在使用VS Code扩展样式组件来表示类似情感的CSS-in-JS库,即使它们并非都具有相同的功能。我甚至看到提议功能的API选择受到保留语法突出显示的目标的影响!
未来
有两个新的CSS-in-JS库,Linaria和astroturf,它们通过将CSS提取到文件中来管理零运行时。 它们的API类似于样式组件,但它们的功能和目标各不相同。
Linaria的目标是通过内置函数(如作用域,嵌套和供应商前缀)来模仿CSS-in-JS库的API,如样式组件。 相反,astroturf是基于CSS模块构建的,具有有限的插值功能,并鼓励使用CSS生态系统而不是使用JavaScript。
结论
CSS-in-JS是一体化的样式解决方案,用于弥合CSS和JavaScript之间的差距。 它们易于使用,并且包含有用的内置优化 - 但所有这些都需要付出代价。 最值得注意的是,通过使用CSS-in-JS,我们基本上从CSS生态系统中退出并使用JavaScript来解决我们的问题。