一、概述
整个前端已经是组件化的天下,而CSS的设计就不是为组件化而生的,所以在目前组件化的框架中都在需要一种合适的CSS解决方案
在组件化中选择合适的CSS解决方案应该符合以下条件:
可以编写局部css:css具备自己的具备作用域,不会随意污染其他组件内的元素;
可以编写动态的css:可以获取当前组件的一些状态,根据状态的变化生成不同的css样式;
支持所有的css特性:伪类、动画、媒体查询等;
编写起来简洁方便、最好符合一贯的css风格特点;
等等...
React官方并没有给出在React中统一的样式风格:
由此,从普通的css,到css modules,再到css in js,有几十种不同的解决方案,上百个不同的库
大家一直在寻找最好的或者说最适合自己的CSS方案,但是到目前为止也没有统一的方案
二、写法 - 内联样式CSS
1. 概念
内联样式是官方推荐的一种css样式的写法:
style 接受一个采用小驼峰命名属性的 JavaScript 对象 ( { {} } ),而不是 CSS 字符串
并且可以引用state中的状态来设置相关的样式
2. 优点
内联样式, 样式之间不会有冲突
可以动态获取当前state中的状态
3. 缺点
写法上都需要使用驼峰标识
某些样式没有提示
大量的样式, 代码混乱
某些样式无法编写(比如伪类/伪元素)
官方是希望内联合适和普通的css来结合编写
4. 代码
import React, { PureComponent } from 'react';
export class App extends PureComponent {
getPColor() {
return 'purple';
}
getStyle() {
return {
color: 'yellow',
fontSize: '50px'
};
}
render() {
const hSize = 40;
return (
<div>
{/* 1. 直接写入 */}
<h1 style={{ color: 'red' }}>React App</h1>
{/* 2. 变量控制 */}
<h2 style={{ fontSize: `${hSize}px` }}>React App</h2>
{/* 3. 三元表达式 */}
<p style={{ color: 30 > 40 ? 'red' : 'blue' }}>React content</p>
{/* 4. 单个样式函数 */}
<p style={{ color: this.getPColor() }}>React content</p>
{/* 5. 样式函数 */}
<p style={this.getStyle()}>React content</p>
</div>
);
}
}
export default App;
三、写法 - 普通CSS文件
普通的css通常会编写到一个单独的文件,之后再进行引入
这样定义的css文件,不管在哪里引入,都是全局的css
这样的编写方式和普通的网页开发中编写方式是一致的
如果按照普通的网页标准去编写,那么也不会有太大的问题
但是组件化开发中希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响
但是普通的css都属于全局的css,样式之间会相互影响
这种编写方式最大的问题是样式之间会相互层叠掉
css代码
.notice{
/* 使用传递过来的变量 */
color: var(--color);
font-size: var(--fontSize);
background-color: black;
}
组件使用
import React, { PureComponent } from 'react';
// 导入样式
import './style.css';
export class index extends PureComponent {
render() {
const fontSize = '40';
return (
<div>
{/* 传递变量到css文件 */}
<h1 className='notice' style={{ '--fontSize': `${fontSize}px`, '--color': 'pink' }}>
React title
</h1>
</div>
);
}
}
export default index;
四、写法 - CSS Module
css modules确实解决了局部作用域的问题,也是很多人喜欢在React中使用的一种方案。
css modules并不是React特有的解决方案
而是所有使用了类似于webpack配置的环境下都可以使用的
如果在其他项目中使用它,那么需要进行配置
比如配置webpack.config.js中的modules: true等
React的脚手架已经内置了css modules的配置:
.css/.less/.scss 等样式文件都需要修改成 .module.css/.module.less/.module.scss 等
之后就可以引用并且进行使用了
css代码
文件名称得改 => 比如 App.module.css
.title{
color: red;
font-size: 32px;
}
.content{
color: blue;
font-size: 24px;
}
组件使用
import React, { PureComponent } from 'react';
// 1. 这样导入
import appStyle from './App.module.css';
export class App extends PureComponent {
render() {
return (
<div>
{/* 2. 这样使用,可使用多个 */}
<h1 className={`${appStyle.title} ${appStyle.text}`}>React title</h1>
<h2 className={appStyle.content}>React content</h2>
</div>
);
}
}
export default App;
缺点
引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的
所有的className都必须使用{style.className} 的形式来编写
不方便动态来修改某些样式,依然需要使用内联样式的方式
五、写法 - Less的编写方式
使用less需要进行配置
1. 配置webpack,可以通过npm run eject => 暴露出webpack的配置
2. 使用carco => create-react-app confg 工具,进行配置
使用工具 - craco
安装
npm install @craco/craco => 安装工具
npm install craco-less => 加载less样式和修改变量
配置
package.js启动命令修改,用craco启动
"scripts": {
"serve": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
新建 craco.config.js 文件
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
// 注入变量
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true
}
}
}
}
]
};
重新启动项目 => npm run serve
less代码
创建App.less 文件,less中可以使用连接符(.home-title)
@redColor:red;
@purpleColor:purple;
.box{
.title{
font-size: 20px;
color: @redColor;
&-active{
color: @purpleColor;
}
}
.content{
font-size: 16px;
// 使用craco.config.js中配置的@primary-color
color: @primary-color;
}
}
组件使用
import React, { PureComponent } from 'react';
import styles from './App.module.less';
export class App extends PureComponent {
render() {
return (
<div className={styles.box}>
<h2 className={`${styles.title} ${styles['title-active']}`}>title</h2>
<p className={`${styles.content}`}>content</p>
</div>
);
}
}
export default App;
六、写法 - CSS in JS
1. 前言
概念
官方文档也有提到过CSS in JS这种方案:
“CSS-in-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义
注意此功能并不是 React 的一部分,而是由第三方库提供
CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式
并且可以方便的使用JavaScript的状态
CSS-in-JS通过JavaScript来为CSS赋予一些能力
包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等
React有被人称之为 All in JS
CSS-in-JS是React编写CSS最为受欢迎的一种解决方案
目前比较流行的CSS-in-JS的库 :
styled-components - 更流行
emotion
glamorous
安装styled-components => npm install styled-components
题外话 - 标签模版字符串
安装vscode插件
插件 : vscode-styled-components
2. 基本使用
创建style.js文件
import styled from 'styled-components';
// 这里可以单独创建一个文件,设置统一变量
const color = 'red'
/**
* AppWrapper
* 用标签模版字符串来调用该函数 => 类似styled.div()这样的调用
* 该函数返回一个React组件
*/
export const AppWrapper = styled.div`
.box {
.title {
color: ${color};
cursor: pointer;
&:hover {
color: orange;
}
}
.content {
color: blue;
}
}
`;
组件使用
import React, { PureComponent } from 'react';
import { AppWrapper } from './style.js';
export class App extends PureComponent {
render() {
return (
// <div className='app'> 变成了下面的写法,因为在style.js中已经写了
<AppWrapper>
<div className='box'>
<h2 className='title'>title</h2>
<p className='content'>content</p>
</div>
</AppWrapper>
// </div>
);
}
}
export default App;
效果
3. 传递props
可以有效的解决动态样式的问题
组件传递,在样式中通过函数拿到传入的值,如果未传入,会使用默认的值
组件使用
import React, { PureComponent } from 'react';
import { AppWrapper } from './style.js';
export class App extends PureComponent {
constructor() {
super();
this.state = {
color: 'green',
fontSize: '60'
};
}
render() {
const { color, fontSize } = this.state;
return (
// 这样写的话,AppWrapper组件就可以接收到color和fontSize这两个属性了
<AppWrapper color={color} fontSize={fontSize}>
<div className='box'>
<h2 className='title'>title</h2>
<p className='content'>content</p>
</div>
</AppWrapper>
);
}
}
export default App;
style.js文件
import styled from 'styled-components';
export const AppWrapper = styled.div`
.box {
.title {
/* props会作为一个函数的参数,在这里拿到 */
color: ${(props) => props.color};
cursor: pointer;
&:hover {
color: orange;
}
}
.content {
font-size: ${({ fontSize }) => fontSize}px;
color: blue;
}
}
`;
4. 设定attrs
可对传过来的值进行处理,attr( 这里是一个函数,后返回一个对象 )
import styled from 'styled-components';
export const AppWrapper = styled.div.attrs((props) => ({
// 通过props拿到传入的值
// 并且可以做一些处理,color,可以通过props.color,并且当color没有传入时,可以设置默认值
color: props.color || 'red',
// 还可以直接定义一些默认值
border: '1px solid red'
}))`
.box {
.title {
/* 使用的是上方attrs中处理过的值 */
color: ${(props) => props.color};
cursor: pointer;
/* 使用attrs中定义的值 */
border: ${(props) => props.border};
}
.content {
font-size: ${({ fontSize }) => fontSize}px;
color: blue;
}
}
`;
5. styled高级特性
七、添加class
1. vue中
2. react中
React在JSX给了开发者足够多的灵活性,但是一旦动态的类多了后,就不太友好了
render() {
const index = 3;
return (
<div className={styles.box}>
{/* 使用 && */}
<h2 className={`${styles.title} ${index === 3 && styles['title-active']}`}>title</h2>
{/* 使用 三元运算符 */}
<p className={`${styles.content} ${index === 2 ? styles['title-active'] : ''}`}>content</p>
</div>
);
}
可使用一个第三方的库: classnames => npm i classnames
import React, { PureComponent } from 'react';
import styles from './App.module.less';
// 1. 使用classnames
import classnames from 'classnames';
export class App extends PureComponent {
render() {
const index = 3;
return (
<div className={styles.box}>
{/* 使用 && */}
<h2 className={`${styles.title} ${index === 3 && styles['title-active']}`}>title</h2>
{/* 使用 三元运算符 */}
<p className={`${styles.content} ${index === 2 ? styles['title-active'] : ''}`}>content</p>
{/* 2. 使用classnames,调用classnames()函数即可 */}
<p
className={classnames('aaa', 'bbb', styles.title, {
[styles['title-active']]: index === 3
})}>
content
</p>
</div>
);
}
}
export default App;