为什么会有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)
}