1、react中样式声明方式
内联样式
import { PureComponent, ReactElement } from 'react';
class App extends PureComponent {
public render(): ReactElement {
return (
// 注意: 内联样式是城小驼峰的方式进行命名
<div style={{ color: 'red', backgroundColor: 'yellow' }}>this is App</div>
);
}
}
export default App;
缺点:驼峰命名,没有提示,大量的样式代码会导致代码混乱,并且无法编写特定的样式(如伪类样式)
直接引入整个css文件
import './index.css' <div className='app'></div>
缺点:样式容易产生冲突,产生覆盖的问题
jss模块化引入组件
import style from './index.module.css' <div className={style.app}></div>
注意:通常来讲使用该方法配置时,js文件与css会放在同一位置,并且为了更好的模块化,在css命名时,需要命名为***.module.css的格式
以上两种方法是当前用的比较广泛的方法,但是各有利弊
第一种方法,简单易用,使用方法与原生css的写法没有区别,但是有可能会造成css样式的全局污染和冲突
第二种方法从本质上隔离了css样式的冲突,但是配置起来略显麻烦,同时class的名称是由js动态生成的,可能会对网站的调试造成一定的麻烦,但是从组件化的思路出发,该方式更贴合组件化的思想。
ts中使用第二种方式报错解决方案
在项目的src目录下,定义custom.d.ts名字除了后缀,其他的可以任意取,内容如下
declare module "*.css"{ const content: any; export = content; }
2、css in js介绍
除了上面引入css文件外,还有一种在react代码中常用的css书写方式, css in js不是指某一个具体的库, 是指组织CSS代码的一种方式, 代表库有styled-components 和 @emotion/styled
传统css的缺陷
1、缺乏模块组织
传统的js和css都没有模块概念,后来在JS界陆续有了Commonjs以及js module, css in js 可以用模块化的方式组织css, 依托于js的模块化方案
2、缺乏作用域
传统的CSS只有一个全局作用域,比如说一个cass可以匹配全局的任意元素。随着项目成长,CSS会变得越来越难以组织,最终导致失控。CsS-in-JS可以通过生成独特的选择符,来实现作用域的效果
3、隐式依赖,样式难以追踪
4、没有变量
5、css与html的耦合度较高
目前比较流程的css in js库有
- styled-component
- emotion
- glamorous
在使用vscode进行styled-components编写时,可以安装插件 vscode-styled-components 进行高亮显示,如果在webstorm或者phpstorm中使用,添加高亮插件style-component
安装 styled-components依赖
npm i styled-components --save
npm i classnames // 实现类名添加去除的判断逻辑,具体见以下例子
3、styled-components的使用
常规使用
import { PureComponent, ReactElement } from 'react';
import styled from 'styled-components';
const AppStyle = styled.div`
.container {
border: 1px solid red;
&_header {
background: yellow;
&:hover {
background-color: pink;
}
}
}
`;
const AppContentStyle = styled.div`
.content {
color: red;
}
`;
class App extends PureComponent {
public render(): ReactElement {
return (
<AppStyle>
<div className="container">
<div className="container_header">this is header</div>
<AppContentStyle>
<div className="content">this is content</div>
</AppContentStyle>
</div>
</AppStyle>
);
}
}
export default App;
组件传参给样式
import { PureComponent, ReactElement } from 'react';
import styled from 'styled-components';
const borderColor = "pink"
interface IAppStyleProp {
color: string;
active: string;
}
const AppStyle = styled.div<IAppStyleProp>`
.container {
border: 1px solid ${borderColor};
&_header {
background: ${(props) => props.color};
&:hover {
background-color: ${(props) => props.active};
}
}
}
`;
class App extends PureComponent {
public render(): ReactElement {
return (
<AppStyle color="red" active="yellow">
<div className="container">
<div className="container_header">this is header</div>
<div className="content">this is content</div>
</div>
</AppStyle>
);
}
}
export default App;
在样式中定义默认值以及配置相关属性
import { PureComponent, ReactElement } from 'react';
import { styled } from 'styled-components';
const borderColor = 'pink';
interface IAppStyleProp {
color: string;
acolor: string;
}
const AppStyle = styled.div
.withConfig({ shouldForwardProp: (props) => props !== 'acolor' }) // 这个表示是css中的属性,不希望往组件下面传,也是消除警告的一个方法
.attrs<IAppStyleProp>((props) => ({ // 这个是配置默认值,如果没有传或者传的是空值,那么就使用默认值
color: props.color || 'pink',
acolor: props.acolor || 'green',
}))<IAppStyleProp>`
.container {
border: 1px solid ${borderColor};
&_header {
background: ${(props) => props.color};
&:hover {
background-color: ${(props) => props.acolor};
}
}
}
`;
class App extends PureComponent<{}, IAppStyleProp> {
public state: IAppStyleProp = { color: 'red', acolor: 'yellow' };
public render(): ReactElement {
const { color, acolor } = this.state;
return (
<AppStyle color={color} acolor={acolor}>
<div className="container">
<div className="container_header">this is header</div>
<div className="content">this is content</div>
</div>
</AppStyle>
);
}
}
export default App;
共享的主题变量
共享部份
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
);
root.render(
<ThemeProvider theme={{ themeColor: 'blue' }}>
<App />,
</ThemeProvider>,
);
使用共享的变量
import { PureComponent, ReactElement } from 'react';
import { styled } from 'styled-components';
const AppStyleWrapper = styled.div`
.title {
color: ${(props) => props.theme.themeColor};
}
`;
class App extends PureComponent {
public render(): ReactElement {
return (
<AppStyleWrapper>
<h1 className="title">this is App</h1>
</AppStyleWrapper>
);
}
}
export default App;
样式的继承
import { PureComponent, ReactElement } from 'react';
import { styled } from 'styled-components';
const CommonStyle = styled.div`
color: red;
`;
// 继承了CommonStyle中的样式
const HeaderStyleWrapper = styled(CommonStyle)`
font-size: 28px;
font-weight: 700;
`;
// 继承了CommonStyle中的样式
const ContentStyleWrapper = styled(CommonStyle)`
font-size: 20px;
`;
class App extends PureComponent {
public render(): ReactElement {
return (
<>
<HeaderStyleWrapper>this is App</HeaderStyleWrapper>
<ContentStyleWrapper>this is app content</ContentStyleWrapper>
</>
);
}
}
export default App;
类名的动态操作(这里使用的是classnames这个依赖)
import classNames from 'classnames';
import { PureComponent, ReactElement } from 'react';
import { styled } from 'styled-components';
interface IAppState {
sign: boolean;
}
const AppStyleWrapper = styled.div`
.title {
color: red;
transition: all 0.2s ease;
}
.fix {
font-weight: 900;
font-size: 20px;
transition: all 0.2s ease;
}
`;
class App extends PureComponent<{}, IAppState> {
public state: IAppState = { sign: false };
public render(): ReactElement {
const { sign } = this.state;
return (
<AppStyleWrapper>
<div className={classNames(['testclass', { title: sign }, { fix: !sign }])}>
this is App
</div>
<button onClick={() => this.setState({ sign: !sign })}>切换</button>
</AppStyleWrapper>
);
}
}
export default App;
样式函数的封装
import React from 'react';
import styled from 'styled-components';
interface ButtonVariantProps {
background: string;
borderColor: string;
}
const withButtonVariant = <P extends object>(Component: React.ComponentType<P>, buttonVariantProps: ButtonVariantProps) => {
const { background, borderColor } = buttonVariantProps;
const StyledComponent = styled(Component)<P>`
color: white;
background-color: ${background};
border-color: ${borderColor};
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
&:hover {
background-color: darken(${background}, 10%);
}
`;
return StyledComponent;
};
interface MyButtonProps {
onClick?: () => void;
}
const MyButton = ({ onClick }: MyButtonProps) => {
return <button onClick={onClick}>My Button</button>;
};
const PrimaryButton = withButtonVariant(MyButton, { background: '#007bff', borderColor: 'transparent' });
const DangerButton = withButtonVariant(MyButton, { background: '#dc3545', borderColor: 'transparent' });
const App = () => (
<div>
<PrimaryButton onClick={() => console.log('Primary Button clicked!')}>Primary Button</PrimaryButton>
<DangerButton onClick={() => console.log('Danger Button clicked!')}>Danger Button</DangerButton>
</div>
);
4、react的过渡动画
react的过渡动画使用 react-transition-group来实现, 相关文档在: http://reactcommunity.org/react-transition-group/transition/
//安装
yarn add react-transition-group --save
react-transition-group主要包含四个组件
Transition
该组件是一个和平台无关的组件 (不一定要结合CSS)
在前端开发中,我们一般是结合CSS来完成样式,所以比较常用的是CSSTransition;
CSSTransition
在前端开发中,通常使用CSSTransition来完成过渡动画效果
SwitchTransition
两个组件显示和隐藏切换时,使用该组件
TransitionGroup
将多个动画组件包裹在其中,一般用于列表中元素的动画
CSSTransition的使用(表示一进一出)
js部份
import { PureComponent, ReactElement } from 'react';
import { CSSTransition } from 'react-transition-group';
interface IAppState {
isShow: boolean;
}
class App extends PureComponent<{}, IAppState> {
public state: IAppState = {
isShow: false,
};
public clickEvent(): void {
this.setState({
isShow: !this.state.isShow,
});
}
public render(): ReactElement {
const { isShow } = this.state;
return (
<>
<button onClick={() => this.clickEvent()}>切换</button>
<h2>
<CSSTransition
in={isShow}
unmountOnExit={true} //表示该组件在退出动画结束后被移除掉
classNames="even"
timeout={500} //这里的动画尽量与动画的时间保持一致
appear // 表示当首次渲染需要执行的动画,如果没有的话,可以不添加该属性
>
<span>this is App</span>
</CSSTransition>
</h2>
</>
);
}
}
export default App;
css部份(里面的名称与classNames对应)
/** 首屏动画 */
.even-appear {
transform: translateX(-150px);
}
.even-appear-active {
transform: translateX(0);
transition: transform 2s ease;
}
/* 进入动画 */
.even-enter {
opacity: 0;
}
.even-enter-active {
opacity: 1;
transition: opacity 0.5s ease-in;
}
/* 离开动画 */
.even-exit {
opacity: 1;
}
.even-exit-active {
opacity: 0;
transition: opacity 0.5s ease-out;
}
SwitchTransition的使用(表示状态切换)
js部份
import { PureComponent, ReactElement } from 'react';
import { CSSTransition, SwitchTransition } from 'react-transition-group';
interface IAppState {
isLogin: boolean;
}
class App extends PureComponent<{}, IAppState> {
public state: IAppState = {
isLogin: false,
};
public render(): ReactElement {
const { isLogin } = this.state;
return (
<>
<SwitchTransition mode="out-in">
<CSSTransition
key={isLogin ? 'exit' : 'login'}
classNames="even"
timeout={500}
>
<button onClick={() => this.setState({ isLogin: !isLogin })}>
{isLogin ? '退出' : '登录'}
</button>
</CSSTransition>
</SwitchTransition>
</>
);
}
}
export default App;
css部份
/* 进入动画 */
.even-enter {
transform: translateX(100px);
opacity: 0;
}
.even-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 0.5s ease;
}
/* 离开动画 */
.even-exit {
opacity: 1;
transform: translateX(0);
}
.even-exit-active {
opacity: 0;
transform: translateX(-100px);
transition: all 0.5s ease;
}
TransitionGroup的使用
js部份
import { PureComponent, ReactElement } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
interface IAppState {
list: string[];
}
class App extends PureComponent<{}, IAppState> {
public state: IAppState = {
list: ['javascript', 'php', 'node', 'python', 'java'],
};
public addEvent(): void {
this.state.list.push('typescript');
this.setState({ list: [...this.state.list] });
}
public removeEvent(index: number): void {
const { list } = this.state;
list.splice(index, 1);
this.setState({ list: [...list] });
}
public render(): ReactElement {
const { list } = this.state;
return (
<>
{/* 这里的ul表示TransitionGroup渲染成的目标元素 */}
<TransitionGroup component="ul">
{list.map((item: string, key: number) => (
<CSSTransition key={item + key} classNames="even" timeout={500}>
<li>
{item}
<button onClick={() => this.removeEvent(key)}>删除</button>
</li>
</CSSTransition>
))}
</TransitionGroup>
<button onClick={() => this.addEvent()}>添加</button>
</>
);
}
}
export default App;
css部份与switchTransition是一样的
结合animate库
animate.css动画库集成到react-transation-group动画模块中
网址:https://animate.style/
npm install animate.css
// 引入样式
import 'animate.css';
//自定义的类名(定义动画效果,进场前,进场后直到结束,结束前,结束后)
classNames={{
enter: "animate__animated",
enterActive: "animate__fadeIn",
exit: "animate__animated",
exitActive: "animate__fadeOut",
}}