为什么会有css-in-js。
它是一种将css代码捆绑在JavaScript 代码中的解决方案。旨在解决css的局限性,例如缺乏动态功能,作用域和可移植性。
主要是体现在开发方式上,因为是组件化的开发方式,区别于以前的页面开发,对于css文件的引入,我们更多的是希望对应组件开发时所写的css代码仅在当前组件范围内引进使用生效,这就需要css模拟实现作用域概念。
可移植性主要是体现在我们希望在不同组件当中共用公共部分的css样式,所以可以把公共部分的代码抽离出来写到一个js文件内,在需要的地方引入使用即可,而不必复制文件内容,减少操作错误。
css本身缺乏动态功能,不能根据条件来决定给某一个元素添加什么样的样式。如果我们将css代码写在了JavaScript文件当中,我们就可以利用js的动态功能为元素去动态添加样式。
css-in-js 方案的优缺点
优点:
让css代码拥有独立的作用域,防止css代码泄漏到组件的外部,防止样式冲突。
让组件更具可移植性,实现开箱即用,轻松创建松耦合的应用程序。
让组件更具可重用性,只需编写一次即可,可以在任何地方运行。不仅可以在同一应用程序中重用组件,而且可以在使用相同框架构建的其他应用程序中重用。
让样式具有动态功能,可以将复杂的逻辑应用于样式规则,如果需要创建动态功能的复杂UI,它是理想的解决方案。
缺点:
为项目增加额外的复杂性;
自动生成的选择器大大降低了代码的可阅读性。主要是在于调试时,自动生成的都是随机的字符串。
综合而言,css-in-js方案的优点大于缺点。
babel配置以支持css属性的两种方式
Emotion 库
emotion 库是css-in-js方案具体实施的一个库,它是一个旨在使用JavaScript编写css样式的库。
安装:
npm install @emotion/core @emotion/styled
npm install @emotion/core @emotion/styled
注意:@emotion/core 在新版本中已经更名为 @emotion/react
css属性支持
JSX Pragma
通知babel不再需要将jsx语法转换为 React.createElement() 方法,而是转换为jsx() 方法。
使用emotion |
Input |
Output |
使用前 |
|
|
使用后 |
|
|
导入emotion库并配置使用。
/** @jsx jsx */ // 这个是提供给babel做解释emotion相关代码时配置的固定的特殊格注释
import jsx from '@emotion/core'; //`@emotion/core` 现在更名为`@emotion/react`了, 所以要这样写:`import { jsx } from '@emotion/react'`.
Babel Preset (推荐使用的方式)
npm run eject 弹射出react app项目的底层配置。
安装@emotion/babel-preset-css-prop 预设模块
npm install @emotion/babel-preset-css-prop
在package.json文件中找到babel属性,加入如下内容:
"presets": [
"react-app",
"@emotion/babel-preset-css-prop"
]
配置emotion的 babel预设集就不需要在代码中引入jsx方法以及对应的特殊注释了。
css方法的两种调用方式
String styles
const style = css`
width: 100px;
height: 30px;
background: skyblue;
`
<div css={style}>App works...</div>
object styles
const style = css({
width: "100px",
height: "30px",
background: "skyblue"
})
<div css={style}>App works...</div>
此时的css方法会帮我们把css样式转换成js模块嵌入到组件代码中。
{
"name": "2h52qn-objectStyle",
"styles": "width:100px;height:30px;background:skyblue;label:objectStyle;",
"map": "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9odWlxdWFuZGVuZy9wcm9qZWN0cy9sZy1mZWQtbHAvbXktbW9kdWxlL3JlYWN0LXRlc3Qvc3JjL2NvbXBvbmVudHMvQ291bnRlci9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFlc0IiLCJmaWxlIjoiL1VzZXJzL2h1aXF1YW5kZW5nL3Byb2plY3RzL2xnLWZlZC1scC9teS1tb2R1bGUvcmVhY3QtdGVzdC9zcmMvY29tcG9uZW50cy9Db3VudGVyL2luZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuLy8gLyoqIEBqc3gganN4ICovXG4vLyBpbXBvcnQgeyBqc3ggfSBmcm9tICdAZW1vdGlvbi9yZWFjdCdcbmltcG9ydCB7IG9ic2VydmVyIH0gZnJvbSAnbW9ieC1yZWFjdC1saXRlJ1xuaW1wb3J0IHsgdXNlUm9vdFN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUnXG5pbXBvcnQgeyBjc3MgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCdcblxuZnVuY3Rpb24gQ291bnRlciAocHJvcHMpIHtcbiAgY29uc3QgeyBjb3VudGVyU3RvcmUgfSA9IHVzZVJvb3RTdG9yZSgpXG4gIGNvbnN0IHsgY291bnQsIGluY3JlbWVudCwgZGVjcmVtZW50IH0gPSBjb3VudGVyU3RvcmVcbiAgY29uc3Qgc3RyaW5nU3R5bGUgPSBjc3NgXG4gICAgd2lkdGg6IDEwMHB4O1xuICAgIGhlaWdodDogMzBweDtcbiAgICBiYWNrZ3JvdW5kOiBza3libHVlO1xuICBgXG4gIGNvbnN0IG9iamVjdFN0eWxlID0gY3NzKHtcbiAgICB3aWR0aDogMTAwLFxuICAgIGhlaWdodDogMzAsXG4gICAgYmFja2dyb3VuZDogJ3NreWJsdWUnXG4gIH0pXG4gIGNvbnNvbGUubG9nKHN0cmluZ1N0eWxlLCBvYmplY3RTdHlsZSlcbiAgcmV0dXJuIChcbiAgICA8c2VjdGlvbj5cbiAgICAgIDxkaXYgY3NzPXtzdHJpbmdTdHlsZX0+QXBwIHdvcmtzLi4ud2l0aCBzdHJpbmcgU3R5bGVzPC9kaXY+XG4gICAgICA8ZGl2IGNzcz17b2JqZWN0U3R5bGV9PkFwcCB3b3Jrcy4uLndpdGggb2JqZWN0IFN0eWxlczwvZGl2PlxuICAgICAgPGg0IHN0eWxlPXt7IHdpZHRoOiAxMDAsIGJhY2tncm91bmQ6ICdibHVlJyB9fT7orqHmlbDlmajmoYjkvos8L2g0PlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBkZWNyZW1lbnQoKX0+LTE8L2J1dHRvbj5cbiAgICAgIDxzcGFuXG4gICAgICAgIHN0eWxlPXt7XG4gICAgICAgICAgd2lkdGg6ICcxMDBweCcsXG4gICAgICAgICAgZGlzcGxheTogJ2lubGluZS1ibG9jaycsXG4gICAgICAgICAgdGV4dEFsaWduOiAnY2VudGVyJyxcbiAgICAgICAgICBmb250U2l6ZTogJzIwcHgnXG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIHtjb3VudH1cbiAgICAgIDwvc3Bhbj5cbiAgICAgIDxidXR0b24gb25DbGljaz17KCkgPT4gaW5jcmVtZW50KCl9PisxPC9idXR0b24+XG4gICAgPC9zZWN0aW9uPlxuICApXG59XG5cbmV4cG9ydCBkZWZhdWx0IG9ic2VydmVyKENvdW50ZXIpXG4iXX0= */"
}
并且会通过类的方式来使用。
<div class="css-2h52qn-objectStyle">App works...with object Styles</div>
emotion中css属性优先级
props对象中的css属性优先级高于组件内部的css属性。
在调用组件时可以覆盖
覆盖组件默认样式。
styled components 样式话化组件
样式话化组件就是用来构建用户界面的,是emotion库听过库提供的另一种为元素添加样式的方式。
1. 创建样式化组件(styled.xxx)
使用前需要引入styled 支持库@emotion/styled
string styles
const button = styled.button`
width: 300px;
height: 30px;
background: skyblue;
`;
// 拓展:还可以根据props属性覆盖样式
// 例如: background: ${props => props.bgColor || 'skyblue'};
object styles
const button = styled.button(props => ({
color: props.color || 'red',
width: 300,
height: 30,
background: 'pink'
}))
2. 为任何组件添加样式
同样有string styles和object styles的区别。
const Demo = ({className}) => <div className={className}> </div>
const Fancy = styled(Demo)`
color: red;
`
3. 为特定父级下的子组件添加样式
通过父组件设置子组件样式
同样有string styles和object styles的两种方式。
const Child = styled.div`
color: red
`
const Parent = styled.div`
${Child}{
color: green
}
`
<Parent><Child/></Parent>
const Child = styled.div({
color: 'red'
})
const Parent = styled.div({
[Child]:{
color: 'yellow'
}
})
<Parent><Child/></Parent>
4. css选择器&:嵌套选择器
& 表示组件本省。
const Container = styled.div`
color: red;
& > a {
color: pink;
}
`
5. 样式化组件的as 属性
要使用组件中的样式,但要改变呈现的元素,可以使用as 属性。
const Button = styled.button`
color: red
`
<Button as="a" href="/#">Click me</Button>
6. 样式组合
在样式组合中,后调用的样式优先级高于先调用的样式。
const base = css`
color: skyblue;
`
const danger = css`
color: red;
`
<button css={[base, danger]}>样式组合的button</button>
7. 全局样式Global
全局样式的应用需要使用到emotion当中的Global组件,及其 styles属性。
const styles = css`
body: {
margin:0;
background: tomato;
color: skyblue;
}
`
function App() {
return <>
<Global styles={styles}>
app is working...
</>
}
8. 使用keyframes方法定义关键帧动画
在emotion中定义帧动画需要用到keyframes 方法。
const move = keyframes`
0% {
background: skyblue;
left: 0;
top: 0;
font-size: 1rem;
}
100% {
background: green;
left: 600px;
top: 300px;
font-size: 2rem;
}
`
const box = css`
color: red;
width: 200px;
height: 200px;
position: absolute;
animation: ${move} 2s ease;
`
<div css={box}>animate box</div>
9. emotion 主题
下载模块:
npm install emotion-theming
引入ThemeProvider
import {ThemeProvider} from 'emotion-theming' // 现在也合并到了@emotion/react中
添加主题内容
ThemeProvider应该放在所有组件的最外层。
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
// import { Provider } from 'react-redux'
// import store from './store'
import { Global } from '@emotion/react'
import styles from './styles'
import { ThemeProvider } from '@emotion/react'
const theme = {
color: {
primary: 'tomato'
}
}
ReactDOM.render(
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>,
document.getElementById('root')
)
获取主题内容
在内部组件中可以通过一下方式获取主题theme的内容。
const primaryColor = props => css`
color: ${props.color.primary};
`
<div css={primaryColor}></div>
也可以使用钩子函数useTheme() 的方式来获取主题内容
import { useTheme } from '@emotion/react'
function Demo() {
const theme = useTheme()
console.log(theme)
}