even

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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",
}}

 

 

posted on 2021-04-11 23:47  even_blogs  阅读(977)  评论(0编辑  收藏  举报