你需要知道的CSS-in-JS

什么是CSS-in-JS?直接在.css文件里写CSS(CSS-in-CSS)不是挺好的吗,为什么还需要CSS-in-JS?

说到这里,不得不提到组件化。因为有了组件化概念,就不再需要维护一大堆杂乱的样式。CSS-in-JS在组件层面(而不是文档层面)对CSS进行了抽象。在项目中维护一个巨大的样式文件夹实在是一件让人感到头疼的事情,很多人不禁感叹,是否还有其他更好的办法?不用说,CSS-in-JS在目前看来就是一个最佳解决方案。

当然,要继续使用CSS-in-CSS还是转向CSS-in-JS完全取决于开发者自己,最关键的是选择合适的工具来改进开发工作流,在提升生产力的同时也让开发变得更有趣。

什么是 CSS-in-JS

JSS对CSS进行了抽象,使用了JavaScript,以声明和可维护的方式来描述样式。它是一个高性能、运行在服务器端的运行时JS到CSS编译器。它的核心部分与框架无关,经过压缩后只有6KB,并可以通过插件API的方式进行扩展。

需要注意的是,内联样式和CSS-in-JS是完全不一样的!

内联样式

const textStyles = {   color: white,   backgroundColor: black
}

<p style={textStyles}>inline style!</p>

在浏览器里,样式属性会被附加到DOM节点上:

<p style="color: white; backgrond-color: black;">inline style!</p>

CSS-in-JS

import styled from 'styled-components';

const Text = styled.div`   color: white,   background: black
`

<Text>Hello CSS-in-JS</Text>

在浏览器里,样式类会被附加到DOM节点上:

<style>
.hash136s21 {   background-color: black;   color: white;
}
</style>

<p class="hash136s21">Hello CSS-in-JS</p>

可以看出,CSS-in-JS会附加整个<style>标签的内容,而内联样式只会把属性附加到DOM节点上。

这有什么实际意义?

并不是所有的CSS特性在JavaScript里都有与之对应的事件处理器,很多伪类选择器(如:disabled、:before、:nth-child)是不受支持的,当然也不支持在html和body标签上应用样式。

而CSS-in-JS可以发挥CSS所有的特性,因为它会生成实际的CSS,这也就可以使用任意的选择器。一些包(如jss、styled-components)甚至支持嵌套!

使用CSS-in-JS有什么好处?

  • 面向组件——不再需要维护一大堆乱糟糟的样式,CSS-in-JS在组件层面(而不是文档层面)对CSS进行了抽象。
  • CSS-in-JS利用JavaScript生态系统的强大能力来增强CSS。
  • “真正的规则隔离”——对选择器定义作用域还远远不够。如果没有显式定义,CSS的属性就会自动继承自父元素,而使用jss-isolate插件就可以防止JSS规则继承属性。
  • 选择器作用域——CSS只有一个全局的命名空间,所以是无法避免出现选择器冲突的。或许BEM这样的命名规范对单个项目来说能起到作用,但要与第三方代码集成时就不一定了。JSS在将JSON编译成CSS时会自动生成唯一的类名。
  • 浏览器引擎前缀——CSS规则自动添加了浏览器引擎前缀。
  • 代码共享——在JS和CSS之间共享常量和函数。
  • 只生成页面会用到的样式。
  • 移除无效代码。
  • CSS单元测试。

CSS-in-JS的不足

  • 学习曲线。
  • 新的依赖。
  • 新手难以将其应用在项目中,他们需要学习更多的东西。
  • 挑战现有标准(这个其实算不上是个缺陷)。

但从总体上看,优势还是远远盖过它的缺点。

最为流行的几个CSS-in-JS框架

Styled Components

import React, { Component } from 'react';
import styled from 'styled-components';

const Title = styled.h1`   color: white;
`;

const Wrapper = styled.div`     background: black
`

class App extends Component {   render() {     return (         <Wrapper>             <Title>Hello World!</Title>         </Wrapper>     );   }
}

export default App;

JSS-React

import React from 'react'
import injectSheet from 'react-jss'

const styles = {     wrapper: {         background: 'black'     },     title: {         color: 'white'     }
}


const App = ({classes}) => (     <div className={classes.wrapper}>         <h1 className={classes.title}>             Hello JSS-React!         </h1>     </div>
)

export default injectSheet(styles)(App)

glamorous

import React from 'react'
import glamorous from 'glamorous'

const Wrapper = glamorous.div({     backgroundColor: 'black'
})

const Title = glamorous.h1({     color: 'white'
})

const App = () => (     <Wrapper>         <Title> Hello JSS-React!</Title>     </Wrapper>
)

export default App;

Radium

import React, { Component } from 'react';
import Radium from 'radium';

@Radium // decorator
class App extends Component {
	render() {         const styles = {             wrapper: {                 background: 'blue',             }             title: {                 color: 'white'             }         };

		return (             <div style={styles.wrapper}>                 <h1 style={styles.title}>Hello Radium!</h1>             </div>
		);
	}
}

export default Radium(App);

Aphrodite

import React, { Component } from 'react';
import { StyleSheet, css } from 'aphrodite';

const styles = StyleSheet.create({     wrapper: {         backgroundColor: 'red'     },     title: {         backgroundColor: 'blue'     }
});

class App extends Component {     render() {         return (             <div className={css(styles.wrapper)}>                 <h1 className={css(styles.title)}>Hello Aphrodite!<h1>             </div>;         )     }
}

Stylotron

import React, { Component } from 'react';
import { styled } from 'styletron-react';

const Wrapper = styled('div', {     backgroundColor: 'black'
})

const Title = styled('h1', {     color: 'white'
})

class App extends Component {     render() {         return (             <Wrapper>                 <Title>Hello Styletron!<Titleh1>             </Wrapper>;         )     }
}

感谢徐川对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ,@丁晓昀),微信(微信号: InfoQChina )关注我们。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章