项目环境搭建
项目依赖
创建支持 TypeScript 的 React 项目
npx create-react-app my-demo --template typescript
根据 typescript 官网文档的说明,还可以使用下面的命令
npx create-react-app my-demo --scripts-version=react-scripts-ts
css样式初始化的插件
npm install --save normalize.css
处理scss文件
npm install node-sass --save
一个简单的、有条件的绑定多个 className 的 JavaScript 实用函数
npm install classnames
@types 支持全局和模块类型定义
npm install @types/classnames --save
tricks 布局 css-tricks.com/
项目目录结构
my-demo
|—— node_modules
|—— public
| └─ favicon.ico
| └─ index.html
| └─ manifest.json
|—— src
| └─ ...
|─ .gitignore
|─ package.json
|─ package-lock.json
|─ README.md
└─ tsconfig.json //文件中指定了用来编译这个项目的根文件和编译选项
创建一个组件
在项目中 删除src
目录下除src/index.tsx
之外所有的文件
import React from 'react';
import ReactDOM from 'react-dom/client';
import Hello from './src/Hello'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<div>hellow TS</div>
);
在src
下创建 Hello.tsx
文件
import React form 'react'
//声明 Hello 组件 props 接口类型
interface BaseProps {
message?:string //可选填 string 类型
}
const Hello:FunctionComponent<BaseProps> =(props) => {
/*
FunctionComponent<BaseProps> 接口,接收一个泛型
+ 使用 interface 定义的 BaseProps j接口作为泛型值
+ 组件还可以接收 props.chilren 属性接收组件实例传入的子节点
+ 使用 defaultProps 为 props 对象中属性设置初始化值
+ React.FunctionComponent 可以简写为 const Hello: FC<BaseProps> = (props) => {}
*/
return <h1>{props.message}</h1>
}
在终端执行 npm start
启动项目查看结果
封装一个Button
组件
Button
按钮需求分析
依赖
classnames: 一个简单的 JavaScript 实用程序,用于有条件地将 classNames 连接在一起
$ npm install classnames --save
$ npm install @types/classnames --save //@types 支持全局和模块类型定义
用于编译css
npm install node-sass --save
classnames 使用示例
/* 与Node.js、Browserify或webpack 一起使用: */
var classNames = require('classnames');
classNames('foo', 'bar'); // => 'foo bar'
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'
/* 与 React.js 一起使用 */
/* 这个包是 的官方替代品classSet,它最初是在 React.js 插件包中提供的。
它的主要用例之一是使动态和条件className道具更易于使用(尤其是比条件字符串操作更简单)。因此,您可能有以下代码来className为<button>React 中的a生成道具: */
class Button extends React.Component {
// ...
render () {
var btnClass = 'btn';
if (this.state.isPressed) btnClass += ' btn-pressed';
else if (this.state.isHovered) btnClass += ' btn-over';
return <button className={btnClass}>{this.props.label}</button>;
}
}
/* 您可以将条件类更简单地表示为一个对象: */
var classNames = require('classnames');
class Button extends React.Component {
// ...
render () {
var btnClass = classNames({
btn: true,
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
return <button className={btnClass}>{this.props.label}</button>;
}
}
/*因为您可以将对象、数组和字符串参数混合在一起,所以支持可选的classNameprops 也更简单,因为结果中只包含真实的参数: */
var btnClass = classNames('btn', this.props.className, {
'btn-pressed': this.state.isPressed,
'btn-over': !this.state.isPressed && this.state.isHovered
});
在src
新建components/Button/buttom.tsx
组件
import React,{ ButtonHTMLAttributes, AnchorHTMLAttributes, FC } from 'react'
import classNames form 'classnames'
//声明按钮尺寸-枚举
export enum ButtonSize {
Large = 'lg',
Small = 'sm'
}
//声明按钮样式-枚举
export enum ButtonType{
Primary = 'primary',
Default = 'default',
Danger = 'danger',
Link = 'link'
}
//声明按钮组件 props 接口
interface BaseButtonProps {
className?: string;
/*设置 Button的禁用*/
disabled?:boolean;
/*设置 Button的尺寸*/
size?:ButtonSize;
/*设置 Button 的类型*/
btnType?:ButtonType;
children: React.ReactNode; //ReactNode reactnode节点
/*设置A标签href的类型*/
href?:string;
}
//声明按钮与超链接标签的原生事件
type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>
type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>
export conast Button:FC<ButtonProps> = (props) =>{
const {
btnType, //传递进来按钮样式属性
className, //传递进来自定义样式属性
disabled, //传递进来是否禁用属性
size,
children,
href,
...restProps //解析按钮与超链接的原生事件属性
} = props;
/*样式拼接处理*/
const classes = classNames('btn', className, {
/*[`btn-${btnType}`] : boolean
boolean:
+ 为真返回 [`btn-${btnType}`]
+ 为假 不返回任何内容
*/
[`btn-${btnType}`]: btnType,
[`btn-${size}`]:size,
'disabled':(btnType === 'link') && disabled //如果传递btnType的属性值为link并设置disabled属性,按钮就是禁用状态。
});
if(btnType === "link" && href){
return (
<a
className={classes}
href={href}
{...restProps} //解析按钮与超链接的原生事件属性
>
{children}
</a>
)
}else{
return(
<button
className={classes}
disabled={disabled}
{...restProps}
>
{children}
</button>
)
}
}
/*定义 props 的默认值*/
Button.defaultProps = {
disabled:false,
btntype:ButtonType.Default,
btntype:ButtonSize.Large,
}
export default Button;
添加默认样式
npm install --save normalize.css
在src
目录先新建styles
文件夹,在styles
文件夹下新建index.css | index.scss
文件
在styles/index.css
文件中引入normalize.css & components/Button/buttom.css
在src/index.tsx
文件中引入styles/index.css
import React from 'react';
import ReactDOM from 'react-dom/client';
import './styles/index.scss'
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
在src
新建components/Button/buttom.css | buttom.scss
组件
.btn {
position: relative;
display: inline-block;
font-weight: 400;
line-height: 1.5;
white-space: nowrap;
text-align: center;
vertical-align: middle;
background-image: none;
border: 1px solid transparent;
border-radius: 0.25rem;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
cursor: pointer;
}
.btn-danger {
color: #fff;
background: #dc3545;
border-color: #dc3545;
}
.btn-primary {
color: #fff;
background: #0d6efd;
border-color: #0d6efd;
}
.btn-lg {
padding: 0.5rem 1rem;
font-size: 1.25rem;
border-radius: 0.3rem;
}
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
border-radius: 0.2rem;
}
.btn-xxx {
width:200px;
height:200px;
}
.btn-link {
font-weight: 400;
color: #0d6efd;
text-decoration: none;
box-shadow: none;
}
.disabled,
.btn[disabled]{
cursor: not-allowed;
opacity: 0.65;
box-shadow: none;
}
在scr/App.tsx
组件中引入Button
组件
import React from 'react';
// 导入Button 组件
import Button,{ButtonType,ButtonSize} from './conponents/Button/button';
/*Button组价可选属性
组件类型
ButtonType.Primary = 'primary'
ButtonType.Default = 'default'
ButtonType.Danger = 'danger'
ButtonType.Link = 'link'
组件大小
ButtonSize.Large = 'lg'
ButtonSize.Small = 'sm'
*/
function App() {
return (
<div className="App">
<Button autoFocus>Hello</Button>
<Button className='btn-xxx'>Hello</Button>
<Button disabled>Disabled Button</Button>
<Button btnType={ButtonType.Primary} size={ButtonSize.Large}>Primary-Lrage-Button</Button>
<Button btnType={ButtonType.Danger} size={ButtonSize.Small}>Danger-Small-Button</Button>
<Button btnType={ButtonType.Link} href='http://www.xxx.com' disabled>被禁用的按钮</Button>
<Button btnType={ButtonType.Link} href='http://www.xxx.com' target='target'>在新窗口打开</Button>
</div>
);
}
export default App;
在当前项目终端组输入npm start
启动项目查看结果
图标解决方案
Fontawesome Font Awesome是一款开源的图标字体库,提供了丰富的图标给Web开发者使用。它支持使用CSS来控制图标的样式和颜色,可以直接在HTML中引用图标,也可以在CSS中使用伪元素来引用。Font Awesome图标具有无缝缩放、无需图像处理、易于自定义等特点,因此被广泛应用于Web开发中。
官网链接fontawesome.com.cn/docs/v5/use…
- 安裝 Font Awesome 库 添加SVG核心 首先需要使用 npm 或者 yan 来安装核心包,核心包包含了使用图标的所有工具
npm i --save @fortawesome/fontawesome-svg-core
- 添加图标包
# Free icons styles
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
- 添加图标包
# Pro icons styles
npm i --save @fortawesome/pro-solid-svg-icons
npm i --save @fortawesome/pro-regular-svg-icons
npm i --save @fortawesome/pro-light-svg-icons
npm i --save @fortawesome/pro-thin-svg-icons
npm i --save @fortawesome/pro-duotone-svg-icons
npm i --save @fortawesome/sharp-solid-svg-icons
npm i --save @fortawesome/sharp-regular-svg-icons
npm i --save @fortawesome/sharp-light-svg-icons
- 添加React组件
npm i --save @fortawesome/react-fontawesome@latest
封装 icon 组件
在src/component
目录下新建Icon
文件夹,在Icon
文件夹中新建icon.tsx
以下是一个Icon组件,它包装了@fortawesome/react-fontawesome包中的FontAwesomeIcon组件。 它包括一些额外的props: - theme?:指定图标的颜色主题 - className:指定要应用于图标的其他类 import语句导入了React,classNames和FontAwesomeIcon等库。 通过定义ThemeProps类型来为可以传递到Icon组件的theme属性定义类型。 export类型在TypeScript中定义接口或类型别名,类似于定义一个类型。 在这里,ThemeProps是一个字符串字面量类型别名,它只能是指定的主题中的其中一个字符串。 接口是动态的“合同”,用于定义对象的需求,在这里,IconProps部分继承了 FontAwesomeIconProps,以便能够使用FontAwesomeIcon自带的props。 此外,IconProps还包含theme属性,其类型为ThemeProps。 在组件内部,通过Object destructuring从props对象中抽取className和theme等属性。 然后使用classNames包为组件基础类名(此处为'viking-icon')、className和theme生成自定义类名。 如果theme属性存在,则更改自定义类名以匹配指定的主题。 最后,使用FontAwesomeIcon组件渲染图标,并传递所有其余props和自定义类名。 export default语句导出Icon组件。
import React from "react";
import classNames from "classnames";
import { FontAwesomeIcon ,FontAwesomeIconProps} from "@fortawesome/react-fontawesome";
// 定义了可传递到Icon组件中的theme属性的类型
export type ThemeProps = 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'light' | 'dark'
// 定义了IconProps接口,其中包含theme属性,同时继承了FontAwesomeIconProps接口
export interface IconProps extends FontAwesomeIconProps {
theme? : ThemeProps
}
// 定义了Icon函数组件,它渲染了一个带有fontawesome图标的React组件
const Icon:React.FC<IconProps> = (props) => {
const {className,theme,...restProps} = props;
const classnames = classNames('viking-icon',className,{
[`icon-${theme}`]:theme
})
return (
<FontAwesomeIcon className={classnames} {...restProps}/>
)
}
// 导出Icon组件
export default Icon;
使用
import React from 'react';
import Icon from './components/Icon';
import { library } from "@fortawesome/fontawesome-svg-core";
import { fas } from "@fortawesome/free-solid-svg-icons";
library.add(fas)
export default function App(){
return(<Icon icon="arrow-down" className='arrow-icon' />)
}
React动画 ReactTransitionGroup
ReactTransitionGroup是一个React官方提供的用于实现动画过渡效果的工具库。它通过管理组件的进入和离开过渡状态来实现动画效果。在使用ReactTransitionGroup时,开发者需要编写两个组件,一个是包装组件,另一个是被包装的组件。包装组件包含ReactTransitionGroup组件和被包装组件,通过钩子函数监听组件状态的改变,从而控制被包装组件的出现和消失。常见的使用场景包括列表项的插入、删除、搜索结果展示等。总之,ReactTransitionGroup是一个功能强大又易于使用的动画库,方便开发者实现优雅的交互体验。
使用ReactTransitionGroup需要执行以下步骤:
- 安装ReactTransitionGroup库:可以使用npm或yarn进行安装,如下所示:
# npm && #typescript
npm install react-transition-group --save
npm install @types/react-transition-group --save
- 导入所需要的组件:在要使用动画过渡效果的组件中,导入所需要的组件,如TransitionGroup、CSSTransition等,如下所示:
import { TransitionGroup, CSSTransition } from 'react-transition-group';
- 渲染目标元素:在组件render方法中,嵌套目标元素,并在其外层包裹TransitionGroup组件,如下所示:
<TransitionGroup>
<CSSTransition
key={show}
in={show}
timeout={300}
classNames="fade"
unmountOnExit
>
<div>Hello, World!</div>
</CSSTransition>
</TransitionGroup>
在这个例子中,我们使用了CSSTransition组件,并嵌套了一个div元素作为目标元素,用于实现淡入淡出的效果。其中,属性解释如下:
- key:必须设置唯一的key,用于React识别不同的组件,从而对其进行过渡效果的处理。
- in:组件是否显示,可以通过状态控制此属性。
- timeout:动画执行时间,单位为毫秒。
- classNames:CSS类名前缀,用于指定动画过渡效果。
- unmountOnExit:是否在退出动画结束后卸载元素,默认为false。
- 配置CSS样式:为了实现具体的动画效果,我们需要在CSS文件中为每个classNames添加具体的样式。这些样式可以通过CSS类名前缀加上对应的状态后缀进行指定,如.fade-enter、.fade-enter-active、.fade-exit等。
以上步骤完成后,就可以在React应用中实现动画过渡效果了。不同的过渡效果,只需要使用不同的CSS样式即可。