React中Css几种实现方案
全局样式
与传统 html 标签类属性不同,react 中 class 必须编写为 className,比如
全局 css
.box {
background-color:red;
width:300px;
height:300px;
}
js
function Hello() {
return <div className="box">hello react</div>
}
ReactDOM.render(<Hello />, document.getElementById('root'))
与传统在 html 标签定义 css 样式不同,因为这不是传统的 html 代码,而是 JSX,由于 class 作为关键字,无法作为标识符出现,比方说下面的代码将会报错。
const { class } = { class: 'foo' } // Uncaught SyntaxError: Unexpected token }
const { className } = { className: 'foo' }
const { class: className } = { class: 'foo' }
关于官方也有对此问题回答
有趣的话题,为什么 jsx 用 className 而不是 class
所以把传统的 html 代码强行搬运到 react 中,如果带有 class 与 style 属性,那么将会报错。
内联样式
内联样式也得写成对象 key-value 形式,遇到-连字符,则需要大写,如
function Hello() {
return (
<div className="box" style={{ fontSize: '32px', textAlign: 'center' }}>
hello react
</div>
)
}
CSS 的font-size
属性要写成fontSize
,这是 JavaScript 操作 CSS 属性的约定。
其实 可传入表达式,比方这里传入的就是{ fontSize: "32px",textAlign: "center" }
对象,也可以将其定义为一个变量传入。
但是写内联样式显得组丑陋影响阅读,并且样式不易于复用,同时伪元素与媒体查询无法实现,但是封装成类样式,又会影响到全局作用域,所以便有了局部样式styles.module.css
。
局部样式 CSS Modules
Css Modules 并不是 React 专用解决方法,适用于所有使用 webpack 等打包工具的开发环境。以 webpack 为例,在 css-loader 的 options 里打开modules:true
选项即可使用 Css Modules。一般配置如下
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
localIdentName: "[name]__[local]___[hash:base64:5]" // 为了生成类名不是纯随机
},
},
然后通过 import 引入
import styles from './styles.module.css'
function Hello() {
return <div className={styles.box}>hello react</div>
}
但如果是有多个局部样式,直接拼接是无效的(毕竟是个无效的表达式)
// 错误
<div className={style.class1 style.class2}</div>
// 正确
<div className={`${style.class1} ${style.class2}`}</div>
<div className={style.class1+ " " +style.class2}</div>
<div className={[style.class1,style.class2].join(" ")}</div>
classnames
还可以通过 npm 包 classnames 来定义类名,如
import classnames from 'classnames'
import styles from './styles.module.css'
;<div className={classnames(styles.class1, styles.class2)}></div>
最终都将编译为
<div class="class1 class2"></div>
当然 classnames 还有多种方式添加,就不列举了,主要针对复杂样式,根据条件是否添加样式。
但是 在 Css Module 中,其实能发现挺多问题的
如果类名是带有-连字符.table-size
那么就只能styles["table-size"]
来引用,并且都必须使用{style.className}
形式。
最主要的是,css 都写在 css 文件中,无法处理动态 css。
CSS in JS
由于 React 对 CSS 的封装非常弱,导致了一系列的第三方库,用来加强 CSS 操作,统称为 CSS in JS,有一种在 js 文件中写 css 代码的感觉,根据不完全统计,各种 CSS in JS 的库至少有47 种,其中比较出名的 便是styled-components。
import styled from 'styled-components'
// `` 和 () 一样可以作为js里作为函数接受参数的标志,这个做法类似于HOC,包裹一层css到h1上生成新组件Title
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
span {
font-size: 2em;
}
`
// 在充分使用css全部功能的同时,非常方便的实现动态css, 甚至 可以直接调用props!
const Wrapper = styled.section`
padding: 4em;
background: ${props => props.bgColor};
`
const Button = styled.a`
/* This renders the buttons above... Edit me! */
display: inline-block;
border-radius: 3px;
padding: 0.5rem 0;
margin: 0.5rem 1rem;
width: 11rem;
background: transparent;
color: white;
border: 2px solid white;
/* The GitHub button is a primary button
* edit this to target it specifically! */
${props =>
props.primary &&
css`
background: white;
color: palevioletred;
`}
`
const App = () => (
<Wrapper bgColor="papayawhi">
<Title>
<span>Hello World</span>, this is my first styled component!
</Title>
<Button
href="https://github.com/styled-components/styled-components"
target="_blank"
rel="noopener"
primary
>
GitHub
</Button>
</Wrapper>
)
像上面的 Title,Wrapper,Button 都是组件,Title 本质就是一个 h1 标签,在通过模板字符串编写局部 css 样式。
能直接编写子元素的样式,以及& :hover
等 Sass 语法。
根据传入属性,在 css 中使用,Wrapper 传入背景颜色属性,Button 判断是否为 primary。
并且能方便的给暴露className
props 的三方 UI 库上样式:
const StyledButton = styled(Button)` ... `
styled-jsx
vercel/styled-jsx: Full CSS support for JSX without compromises (github.com)
styled-jsx 概括第一印象就是 React css 的 vue 解决。yarn add styled-jsx
安装后,不用import
,而是一个 babel 插件,.babelrc
配置:
{
"plugins": [
"styled-jsx/babel"
]
}
使用
render () {
return <div className='table'>
<div className='row'>
<div className='cell'>A0</div>
<div className='cell'>B0</div>
</div>
<style jsx>{`
.table {
margin: 10px;
}
.row {
border: 1px solid black;
}
.cell {
color: red;
}
`}</style>
</div>;
}
只会作用到同级标签作用域,可以说是一种另类的内联样式了,如果不喜欢将样式写在 render 里,styled-jsx 提供了一个 css
的工具函数:
import css from 'styled-jsx/css'
export default () => (
<div>
<button>styled-jsx</button>
<style jsx>{button}</style>
</div>
)
const button = css`
button {
color: hotpink;
}
`
补充:现在我更推荐使用 Emotion。