样式设置
React 可以使用内联样式也可以外部引入css文件,样式设置有class和style两种方式
class类名设置
- 在标签中不能写class,必须写className
- 样式的代码写在css文件中
- className必须接受一个字符串
/* App组件的样式 */
.title-fa {
color: gold;
}
// App.js
import './App.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className='title-fa'>父组件</h1>
<p>{this.state.msg}</p>
<button onClick={this.change}>changefromfather</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
style内联样式
- 不能像原生style属性中一样写成字符串,必须写成对象的形式
- 对于
font-size这种以-连接的css属性,要用小驼峰方式命名
// App.js
import './App.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className='title-fa'>父组件</h1>
{/* 设置内联样式,style只接受一个对象,{{}}双括号意义:外面的单括号表示括号中使用 JavaScript 表达式以及变量,里面或括号表示这是一个对象 */}
<p style={{color:"blue",fontSize:"18px"}}>{this.state.msg}</p>
<button onClick={this.change}>changefromfather</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
react中使用css module
在Vue中可以使用css作用域加上scoped属性来实现组件之间的样式不会互相干扰,实现样式独立。
react中的css没有域的概念,css是全局的,任何一个组件的样式规则,都对整个页面有效。比如当有两个css文件,它们的中的一些样式名是一样的,那么就会被覆盖。简单的解决办法就是将样式的命名变得复杂且不重复,但这样样式多了也很难避免重复,且命名也不会太好看。所以react中需要使用CSS Modules。
CSS Modules 的做法是通过配置将.css文件进行编译,编译后在每个用到css的组件中的css类名都是独一无二的,从而实现CSS的局部作用域。
全局样式
- 全局样式css文件命名规则:
xxx.css - 全局样式引入方式:
import 'xxx.css' - 全局样式用法:
<div className='css文件中命名的类名'>
// App.js
import './App.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className='title-fa son'>父组件</h1>
{/* 设置内联样式,style只接受一个对象,{{}}双括号意义:外面的单括号表示括号中使用 JavaScript 表达式以及变量,里面或括号表示这是一个对象 */}
<p style={{color:"blue",fontSize:"18px"}}>{this.state.msg}</p>
<button onClick={this.change}>changefromfather</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
/* App组件的样式 */
.title-fa {
color: gold;
}
// son.js
import React from "react";
import "./son.css"
class Son extends React.Component {
state = {
sonMsg: "Hello from Son"
}
// 方法:向父组件传递数据
sendMessageToParent = () => {
this.props.onSendMessage(this.state.sonMsg); // 调用父组件传递的方法
};
render() {
// 不写constructor构造函数,采用ES7新写法props会直接绑定在组件实例上
return (
<div>
<h1>子组件</h1>
<p>{this.props.msg}</p>
<button onClick={this.sendMessageToParent}>Send Message to App</button>
</div>
)
}
}
export default Son;
/* Son组件的样式 */
.son {
background-color: black;
}
App组件只引入了
App.css,但实际上使用son.css中的类也生效了,所以任何一个组件的样式规则,都对整个页面有效,则css其实是全局的,并没有只对某一个组件单独生效。
局部样式(css modules)
- 局部样式css文件命名规则:
xxx.module.css,其中module.css是固定写法 - 局部样式引入方式:
import xxx from 'xxx.module.css' - 局部样式用法:
<div className={xxx.在xxx.module.css文件中命名的类名}>,当类名包含连字符,就需要使用方括号来获取,在 JavaScript 对象中连字符号是非法标识符,所以需要使用方括号语法来访问。<div className={xxx["xx-yy"]}>
实际上利用了css的模块化,CSS Module 允许将 CSS 文件视为 JavaScript 模块的一部分。这样就可以像导入 JavaScript 模块一样导入 CSS 文件,并使用导入的CSS文件的类名。
当引入一份以.module.css为css文件后缀时,最后引入的文件内容会输出为一个对象。
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
console.log(appStyle)
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
{/* 设置内联样式,style只接受一个对象,{{}}双括号意义:外面的单括号表示括号中使用 JavaScript 表达式以及变量,里面或括号表示这是一个对象 */}
<p style={{ color: "blue", fontSize: "18px" }}>{this.state.msg}</p>
<button onClick={this.change}>changefromfather</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.fa-bgc {
background-color: purple;
}
css modules会默认给类名加上一个唯一标识符(即哈希字符串),从而实现类名不重复。如上图对象中的属性名是原本在
.module.css文件中定义的类名,属性值是编译后经过模块化打包的新的类名,它加上了哈希字符串。最后页面上所使用的样式的类名也是经过模块化打包所输出 的新的类名
css modules语法
:global(.类名):表示声明一个全局规则。凡是这样声明的类名,都不会被编译成哈希字符串
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.fa-bgc {
background-color: purple;
}
/* 全局样式 */
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
console.log(appStyle)
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
{/* 使用全局样式 */}
<p className="all">{this.state.msg}</p>
<button onClick={this.change}>changefromfather</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
// son.js
import React from "react";
// import sonStyle from "./son.module.css"
class Son extends React.Component {
state = {
sonMsg: "Hello from Son"
}
// 方法:向父组件传递数据
sendMessageToParent = () => {
this.props.onSendMessage(this.state.sonMsg); // 调用父组件传递的方法
};
render() {
// 不写constructor构造函数,采用ES7新写法props会直接绑定在组件实例上
return (
<div>
{/* 使用全局样式 */}
<h1 className="all">子组件</h1>
<p>{this.props.msg}</p>
<button onClick={this.sendMessageToParent}>Send Message to App</button>
</div>
)
}
}
export default Son;
:local(.类名):表示显式的局部作用域,实际上是在告诉CSS Modules这个类名应该是局部的。如果省略 :local,类名默认也是局部的。
/* App组件的样式,App.module.css */
:local(.title-fa) {
color: gold;
}
:local(.fa-bgc) {
background-color: purple;
}
/* 全局样式 */
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
以上代码等同于
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.fa-bgc {
background-color: purple;
}
/* 全局样式 */
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
composes关键字:用于组合多个类名,表示一个选择器可以继承另一个选择器的规则。
/* App组件的样式,App.module.css */
/* 基本的按钮样式 */
.button {
padding: 10px 20px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #fff;
cursor: pointer;
}
/* 特定类型的按钮样式 */
.primary-button {
composes: button; /* 继承 .button 的样式 */
background-color: blue;
color: white;
}
// App.js
import Son from './son';
import { PureComponent } from 'react'
console.log(appStyle)
class App extends PureComponent {
state = {
msg: "父组件的数据"
}
change = () => {
this.setState({
msg: "父组件数据被修改"
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
<button className={appStyle.button} onClick={this.change}>changefromfather</button>
<br />
<button className={appStyle["primary-button"]}>普通按钮</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
在CSS Modules中,甚至可以从不同的CSS模块文件中组合类名,以实现样式的复用。这通常通过 @value 指令来实现,该指令允许从其他CSS模块文件中导入类名,然后在当前文件中使用这些类名进行组合。
/* 基本的按钮样式,Button.module.css */
.button {
padding: 10px 20px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #fff;
cursor: pointer;
}
/* Theme.module.css */
/* 主题样式 */
.primary {
background-color: blue;
color: white;
}
在Son.module.css中组合上面两个不同文件的类名
/* Son组件的样式,Son.module.css */
.son {
background-color: black;
}
/* 在Son.module.css中组合类名 */
@value button from './Button.module.css';
@value primary from './Theme.module.css';
/* 组合按钮样式和主题样式 */
.complex-button {
composes: button primary;
font-weight: bold;
}
使用组合的类名
// son.js
import React from "react";
import sonStyle from "./son.module.css"
class Son extends React.Component {
state = {
sonMsg: "Hello from Son"
}
// 方法:向父组件传递数据
sendMessageToParent = () => {
this.props.onSendMessage(this.state.sonMsg); // 调用父组件传递的方法
};
render() {
// 不写constructor构造函数,采用ES7新写法props会直接绑定在组件实例上
return (
<div>
{/* 使用全局样式 */}
<h1 className="all">子组件</h1>
<p>{this.props.msg}</p>
{/* 使用组合后的类名 */}
<button className={sonStyle["complex-button"]} onClick={this.sendMessageToParent}>Send Message to App</button>
</div>
)
}
}
export default Son;
按钮就应用了 button 和 primary 类名的样式,以及 complex-button 类名的额外样式。
react动态添加样式
对于react中动态绑定样式实质上是通过表达式的结果进行字符串的拼接,它不像vue可以传递字符串、对象以及数组。
动态绑定className
对象语法
传给 className 一个对象,可动态地切换 class。但是使用类似vue、小程序等对象语法是不支持的。
错误写法:
render(){
return <div className={ 'box-color':this.state.isError }>hello world</div>
}
下面这种对象变量写法是不支持的,也不会报错,控制台className显示为[object object],无效。
const classObj = {
'box-show': this.state.isShow,
'box-color': this.state.isError
}
render(){
return <div className={ classObj }>hello world</div>
}
使用逻辑运算符
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.title-red {
color: red;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据",
red:false
}
change = () => {
this.setState({
...this.state,
red:true
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={this.state.red && appStyle['title-red']}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
使用三目运算符
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.box-show {
visibility: visible;
color: red;
}
.box-hide {
visibility: hidden;
}
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={this.state.isShow ? appStyle['box-show'] : appStyle["box-hide"]}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
使用函数
绑定的数据对象也不必定义在{}插值表达式中,可以定义一个函数,将类名返回即可。
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
showHandle = () => {
return this.state.isShow ? appStyle['box-show'] : appStyle["box-hide"]
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={this.showHandle()}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
数组写法
React中不支持className数组语法,错误代码:
.box-red {
color: red;
}
.box-size {
font-size: 20px;
font-weight: 600;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={this.state.isShow?appStyle["box-red"]:[ appStyle["'box-size'"], appStyle['box-red']]}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
控制台显示结果(无效,中间多了个逗号):
不支持数组语法,可以使用三目运算符来表示,将类名拼接成字符串,但会增加css代码量,可用ES6模板字符串,代码如下:
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={this.state.isShow?appStyle["box-red"]:`${appStyle["box-size"]} ${appStyle['box-red']}`}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
控制台显示结果(有效):
<div class="box-sizee box-red"></div>
注:
${appStyle["box-red"]}和${appStyle["box-size"]}中间用空格隔开。
classname库
借助classname库来动态操作类名,该库的本质也是帮助生成类名的字符串,而不需要自己手动拼接。
安装:
npm i classname
classname是一个函数,函数接受一个对象,对象的属性值就是要使用的类名,当属性值为true表示有该类样式,为false表示没有该类样式。最后返回的就是拼接的字符串。
对于全局样式
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.box-red {
color: red;
}
.box-size {
font-size: 20px;
font-weight: 600;
}
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
import classnames from 'classnames'
// 引入的classname是一个函数,函数接受一个对象,对象的属性值就是要使用的类名,当属性值为true表示有该类样式,为false表示没有该类样式
let str =classnames({
all: true,
red: false,
blue:true
});
console.log(str);
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={classnames({
all: this.state.isShow
})}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
控制台打印结果:
当使用了css modules模块化的局部样式就会失效了
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.box-red {
color: red;
}
.size {
font-size: 20px;
font-weight: 600;
}
.blue {
color: blue;
}
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
import classnames from 'classnames'
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={classnames({
size:true,
blue:this.state.isShow
})}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
因为最后绑定的类名还是原本的类名,而css modules模块化的局部样式需要绑定经过编译打包后的类名,是加了hash值唯一的类名,所以使用原本的类名就失效了
正确的做法是要引入classname的bind,使用classnames的bind函数绑定引入的模块名,然后它会返回一个新函数,用这个新函数来动态操作className。
/* App组件的样式,App.module.css */
.title-fa {
color: gold;
}
.box-red {
background-color: blue;
}
.box-bgc {
background-color: black;
}
.size {
font-size: 20px;
font-weight: 600;
}
.blue {
color: #fff;
}
.red {
color: red;
}
:global(.all) {
font-size: 20px;
font-weight: 600;
color: purple;
}
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import Son from './son';
import { PureComponent } from 'react'
import classnames from 'classnames/bind.js'
let bindAppstyleClassname = classnames.bind(appStyle);
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow
});
}
sonchange = (arg) => {
this.setState({
msg: arg
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={bindAppstyleClassname({
size: true,
blue: this.state.isShow,
"box-bgc": !this.state.isShow,
"box-red": this.state.isShow,
red: !this.state.isShow,
})}>{this.state.msg}</p>
<button onClick={this.change}>change</button>
<hr />
<Son msg={this.state.msg} fatherfn={this.sonchange}></Son>
</div>
)
}
}
export default App;
动态绑定style
react中style属性必须接受对象的形式
// App.js
import appStyle from './App.module.css'; // 引入App组件的样式
import { PureComponent } from 'react'
import classnames from 'classnames/bind.js'
let bindAppstyleClassname = classnames.bind(appStyle);
class App extends PureComponent {
state = {
msg: "父组件的数据",
isShow: false,
pur: true
}
change = () => {
this.setState({
msg: '数据被修改',
isShow: !this.state.isShow,
pur: !this.state.pur
});
}
render() {
return (
<div>
{/* className属性使用在App.css定义的类名 */}
<h1 className={appStyle["title-fa"]}>父组件</h1>
<p className={bindAppstyleClassname({
size: true,
blue: this.state.isShow,
"box-bgc": !this.state.isShow,
"box-red": this.state.isShow,
red: !this.state.isShow,
})}>{this.state.msg}</p>
<p style={this.state.pur ? { color: "purple" } : { color: "gold" }}>动态style</p>
<button onClick={this.change}>change</button>
</div>
)
}
}
export default App;