1. 内联样式
-
内联样式是官方推荐的一种css样式的写法:
- style 接受一个采用小驼峰命名属性的 JavaScript 对象,而不是 CSS 字符串
- 并且可以引用state中的状态来设置相关的样式
-
内联样式的优点:
- 内联样式, 样式之间不会有冲突
- 可以动态获取当前state中的状态
-
内联样式的缺点:
- 写法上都需要使用驼峰标识
- 某些样式没有提示
- 大量的样式, 代码混乱
- 某些样式无法编写(比如伪类/伪元素)
-
所以官方依然是希望内联合适和普通的css来结合编写
import React, { PureComponent } from 'react'
export class App extends PureComponent {
constructor() {
super()
this.state = {
titleSize: 30
}
}
addTitleSize() {
this.setState({ titleSize: this.state.titleSize + 2 })
}
render() {
const { titleSize } = this.state
return (
<div>
<button onClick={e => this.addTitleSize()}>增加titleSize</button>
<h2 style={{color: "red", fontSize: `${titleSize}px`}}>我是标题</h2>
<p style={{color: "blue", fontSize: "20px"}}>我是内容, 哈哈哈</p>
</div>
)
}
}
export default App
2. 普通的CSS
-
普通的 css 通常会编写到一个单独的文件,之后再进行引入(通过
import) -
这样的编写方式和普通的网页开发中编写方式是一致的:
- 如果按照普通的网页标准去编写,那么也不会有太大的问题
- 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响
- 但是普通的css都属于全局的css,样式之间会相互影响
-
这种编写方式最大的问题是样式之间会相互层叠掉
3. CSS Module
-
css modules并不是React特有的解决方案,而是所有使用了类似于webpack配置的环境下都可以使用的。
- 如果在其他项目中使用它,那么我们需要自己来进行配置,比如配置webpack.config.js中的modules: true等。
-
React的脚手架已经内置了css modules的配置:
- .css/.less/.scss 等样式文件都需要修改成 .module.css/.module.less/.module.scss 等,之后就可以引用并且进行使用了
-
css modules确实解决了局部作用域的问题,但是这种方案也有自己的缺陷:
- 引用的类名,不能使用连接符(.home-title),在JavaScript中是不识别的
- 所有的className都必须使用{style.className} 的形式来编写
- 不方便动态来修改某些样式,依然需要使用内联样式的方式
-
App.jsx
import React, { PureComponent } from 'react' // 命名方式 import appStyle from './App.module.css' export class App extends PureComponent { render() { return ( <div> <h2 className={appStyle.title}>我是标题</h2> <p className={appStyle.content}>我是内容, 哈哈哈哈</p> </div> ) } } export default App -
App.module.css
.title { font-size: 32px; color: green; } .content { font-size: 22px; color: orange; }
4. Less配置和使用
-
React 脚手架搭建的项目能够引入 less 文件,但是不生效,需要修改 webpack 的配置
-
修改webpack配置的方式
npm run eject:暴露webpack的配置,进行修改(如果修改出错,可能会影响项目)- craco:
- 安装 craco 并修改
package.json里的scripts属性npm install @craco/craco"start": "craco start",
- 安装 craco 并修改
-
安装craco-less:
npm install craco-less,使引入的less文件生效 -
craco.config.js配置
const CracoLessPlugin = require('craco-less'); module.exports = { plugins: [ { plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { // 进行一些全局变量 // modifyVars: { '@primary-color': '#1DA57A' }, javascriptEnabled: true, }, }, }, }, ], }; -
代码演示
- App.jsx
import React, { PureComponent } from 'react' import './App.less' export default class App extends PureComponent { render() { return ( <div className='app'> <div className="section"> <h2 className="title">我是标题</h2> <div className="content">我是内容,我是内容</div> </div> </div> ) } }- App.less
@primaryColor: red; .section { border: 1px solid @primaryColor; .title { font-size: 30px; color: @primaryColor; } .content { font-size: 20px; color: @primaryColor; } } -
注意:文章采用的是 最新的脚手架版本
5.0.1,安装上述依赖会报错,可以使用下面方法解决npm install @craco/craco@alphacraco-less
5. CSS IN JS
-
官方文档也有提到过CSS in JS这种方案:
- “CSS-in-JS” 是指一种模式,其中 CSS 由 JavaScript 生成而不是在外部文件中定义
- 注意此功能并不是 React 的一部分,而是由第三方库提供
- React 对样式如何定义并没有明确态度
-
在传统的前端开发中,我们通常会将结构(HTML)、样式(CSS)、逻辑(JavaScript)进行分离
-
之前的文章有提到过,React的思想中认为逻辑本身和UI是无法分离的,所以才会有了JSX的语法
-
而样式也是属于UI的一部分,事实上CSS-in-JS的模式就是一种将样式(CSS)也写入到JavaScript中的方式,并且可以方便的使用JavaScript的状态
-
所以React有被人称之为 All in JS
-
-
CSS-in-js 好用么?
-
CSS-in-JS通过JavaScript来为CSS赋予一些能力,包括类似于CSS预处理器一样的样式嵌套、函数定义、逻辑复用、动态修改状态等等
-
虽然CSS预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点
-
所以,目前可以说CSS-in-JS是React编写CSS最为受欢迎的一种解决方案
-
-
常见的 CSS-in-JS 库
- styled-components
- emotion
- glamorous
5.1 ES6标签模板字符串
-
基本使用
const name = 'rsquo' const age = 18 const str = `my name is ${name}, age is ${age}` console.log(str) // my name is rsquo, age is 18 -
标签模板字符串
const name = 'rsquo' const age = 18 // 2.标签用法 function foo(...args) { console.log(args); } foo('rsquo', 18, 1.83) // ['rsquo', 18, 1.83] foo`my name is ${name}, age is ${age}`
5.2 styled-components
-
在styled component中,就是通过上面的标签模板字符串来解析,最终生成我们想要的样式的
-
styled-components的本质是通过函数的调用,最终创建出一个组件:
- 这个组件会被自动添加上一个不重复的class
- styled-components会给该class添加相关的样式
-
基本使用
- App.jsx
import React, { PureComponent } from 'react' import { AppWrapper } from './style' export default class App extends PureComponent { render() { return ( <AppWrapper> <div className="section"> <h2 className="title">我是标题</h2> <div className="content">我是内容,我是内容</div> </div> <div className="footer"> <p>免责声明</p> <p>版权声明</p> </div> </AppWrapper> ) } }- style.js
-
它支持类似于CSS预处理器一样的样式嵌套:
- 支持直接子代选择器或后代选择器,并且直接编写样式
- 可以通过
&符号获取当前元素 - 直接伪类选择器、伪元素等
-
推荐在 VSCode 中安装 一个
vscode-styled-components的插件,方便编写css
-
import styled from 'styled-components' export const AppWrapper = styled.div` .section { border: 1px solid red; .title { font-size: 20px; color: blue; &:hover { background-color: skyblue; } } } .footer { border: 1px solid green; } ` -
高级用法
-
子元素单独抽取样式组件
-
props:可以被传递给styled组件
- 获取props需要通过 ${}传入一个插值函数,props会作为该函数的参数
- 这种方式可以有效的解决动态样式的问题
-
attrs:通过attrs给标签模板字符串中提供的属性,可以在未传入 props 时使用
-
从一个单独的文件中引入变量
-
支持样式的继承
-
styled设置主题,通过引入
styled-components中的ThemeProvider组件,将<App/>组件包裹
-
-
高级用法代码演示
- index.js:项目入口文件
import React from 'react'; import ReactDOM from 'react-dom/client'; import { ThemeProvider } from 'styled-components'; import App from './App' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> {/* 使用styled 设置主题 */} <ThemeProvider theme={{color: '#096dd9', size: '30px'}}> <App /> </ThemeProvider> </React.StrictMode> );- App.jsx
import React, { PureComponent } from 'react' import Home from './home' import { AppWrapper, SectionWrapper } from './style' export default class App extends PureComponent { constructor() { super() this.state = { color: 'yellow', size: 30 } } render() { const { color, size } = this.state return ( <AppWrapper> <SectionWrapper color={color} size={size}> <h2 className="title">我是标题</h2> <p className="content">我是内容,我是内容</p> <button onClick={e => this.setState({color: 'blue'})}>修改颜色</button> </SectionWrapper> <Home/> <div className="footer"> <p>免责声明</p> <p>版权声明</p> </div> </AppWrapper> ) } }- style.js
import styled from 'styled-components' // 引入变量中的样式 import { secondColor, largeSize } from './style/varibles' export const AppWrapper = styled.div` .footer { border: 1px solid green; } ` // 子元素单独抽取样式组件 // 通过 attrs 设置默认值 export const SectionWrapper = styled.div.attrs(props => ({ tColor: props.color || 'purple' }))` border: 1px solid red; .title { font-size: ${props => props.size}px; color: ${props => props.tColor}; &:hover { background-color: skyblue; } } .content { font-size: ${largeSize}; color: ${secondColor}; } `- varibles.js
export const primaryColor = "#722ed1" export const secondColor = "#b37feb" export const smallSize = "12px" export const middleSize = "14px" export const largeSize = "18px"- Home.jsx
import React, { PureComponent } from 'react' import { HomeWrapper, YSButtonWrapper } from './style' export default class Home extends PureComponent { render() { return ( <HomeWrapper> <div className='top'> <div className="banner">BannerContent</div> </div> <div className="bottom"> <h2 className='header'>商品列表</h2> <ul className='product-list'> <li className='item'>商品列表1</li> <li className='item'>商品列表2</li> <li className='item'>商品列表3</li> </ul> </div> {/* 继承样式生成的按钮 */} <YSButtonWrapper>按钮</YSButtonWrapper> </HomeWrapper> ) } }- home/style.js
import styled from 'styled-components' // 引入变量 import { primaryColor } from '../style/varibles' const YSButton = styled.button` padding: 4px 12px; border-radius: 5px; ` // 使用继承 export const YSButtonWrapper = styled(YSButton)` background-color: ${primaryColor}; color: #fff; ` export const HomeWrapper = styled.div` .top { .banner { color: red; } } .bottom { .header { /* 使用 ThemeProvider中的变量 */ color: ${props => props.theme.color}; font-size: ${props => props.theme.size}; } .product-list { .item { color: blue; } } } `
6. React 中添加class
-
React在JSX给了我们开发者足够多的灵活性,可以像编写JavaScript代码一样,通过一些逻辑来决定是否添加某些class
-
借助classnames库(推荐)
- 安装:
npm install classnames - 基本用法
classNames('foo', 'bar'); // => 'foo bar' classNames('foo', { bar: true }); // => 'foo bar' classNames({ 'foo-bar': true }); // => 'foo-bar' classNames({ 'foo-bar': false }); // => '' classNames({ foo: true }, { bar: true }); // => 'foo bar' classNames({ foo: true, bar: true }); // => 'foo bar' // lots of arguments of various types classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux' // other falsy values are just ignored classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'- 代码演示
import React, { PureComponent } from 'react' // 引入classnames import classNames from 'classnames' export default class App extends PureComponent { constructor() { super() this.state = { isbbb: false, isccc: true } } render() { const { isbbb, isccc } = this.state // 为了添加样式 const classList = ['aaa'] if(isbbb) classList.push('bbb') if(isccc) classList.push('ccc') const classname = classList.join(' ') return ( <div> {/* 不推荐这种写法,难以阅读 */} <h2 className={`aaa ${isbbb ? 'bbb' : ''} ${isccc ? 'ccc' : ''}`}>哈哈哈</h2> {/* 通过列表添加,但是会有很多js代码 */} <h2 className={classname}>呵呵呵</h2> {/* 使用第三方库classnames */} <h2 className={classNames('aaa', {bbb: isbbb}, {ccc: isccc})}>嘿嘿嘿</h2> <h2 className={classNames(['aaa', {bbb: isbbb}, {ccc: isccc}])}>嚯嚯嚯</h2> </div> ) } } - 安装: