typescript在前端开发中扮演了重要的角色,现在我们从零开始一个react + ts项目,一步一步,在项目中吧一些ts的用法做一些整理说明,希望对于大家在项目实战有所帮助,当然这是一个从基础开始的项目,同样在过程中去穿插一些ts语法讲解,我想将更多的重点放在ts项目中如何去使用,而不是干燥的去说ts类型语法,希望感兴趣的同学们可以收藏,我尽量保证一周一期
1、开始一个新项目
项目构建,这块我们直接使用create-react-app脚手架,在指定目录下执行npx create-react-app typescript --template typescript ,接下来我们就将当前的目录展示给大家,具体如下:
接下来对于目录public src等等这些文件夹及根目录下的文件的用途,我们不做说明,我们今天直说关于typescript相关的内容在react中的使用,接下来,我们将目标聚焦到react-app-env.d.ts,我们看一下下面这段代码:
/// <reference types="react-scripts" />
关于/// <reference types="react-scripts" />这块我们就需要说道历史包袱了,那么我们简单介绍一下,让我们了解ts早期的模块化写法:
- 1、/// 是早期的ts模块化导入的方式,es6模块出来以后就不推荐使用了;
- 2、reference有两个属性
path和types,前者是用来import文件,后者是导入类型文件;
当我们使用/// <reference types="react-scripts" />,他会去node_modules目录下匹配找到对应库的类型,具体规则如下:
- 1、查找
node_modules/@types/react-scripts/index.d.ts类型声明文件; - 2、查找
node_modules/@types/react-scripts/package.json文件中types指定的文件; - 3、查找
node_modules/react-scripts/index.d.ts类型声明文件; - 4、查找
node_modules/react-scripts/package.json文件中types指定的文件;
2、项目中使用css的基本方案
(1)全局css样式编写
我们初始化的项目中包含了一些全局样式(src/index.css),我们可以编写一些全局的样式;
(2)组件的样式
- css文件(例如global样式文件),css-modules文件(我们可以编写xxx.module.css)我们可以讲得到的文件用模块的思维去给给组件className赋值
- 通过style内联的方式配合
React.CSSProperties方式进行组合,有了React.CSSProperties方式,vscode会有很良好的语法提示
import React from 'react';
import logo from './logo.svg';
import './App.css';
const buttonStyles: React.CSSProperties = {
backgroundColor: '#fff',
border: '1px solod #ccc',
textAlign: 'center',
lineHeight: '32px'
}
function App() {
return (
<div>
<img src={logo} className="App-logo" alt="logo" />
{/* 样式测试 */}
<button style={buttonStyles}>测试</button>
</div>
);
}
export default App;
- 使用外部的样式库,例如:tailwindcss、styled-component等等,这里我们用
styled-components为例简单说一下基本使用办法(我们需要安装vscode-styled-components,让编译器有良好的提示功能)
yarn add styled-components
yarn add @types/styled-components -D
我们可以按照如下的方式使用
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Styled from 'styled-components';
const buttonStyles: React.CSSProperties = {
backgroundColor: '#fff',
border: '1px solod #ccc',
textAlign: 'center',
lineHeight: '32px'
}
function App() {
return (
<AppContainer>
<img src={logo} className="App-logo" alt="logo" />
{/* 样式测试 */}
<button style={buttonStyles}>测试</button>
</AppContainer>
);
}
const AppContainer = Styled.div`
background-color: #eee;
height: 100%;
`
export default App;
3、如何定义组件的props写法
通常情况下,我们可以通过type和interface两种方式去定义组件的props,大多数情况下两者是可以互换使用的,
如下所示:
import React from 'react'
import { AppBox } from './styles'
type ColumnProps = {
text: string
}
// 或者
// interface ColumnProps = {
// text: string
// }
const Column: React.FC<ColumnProps> = ({ text }) => {
return (
<AppBox>
{text || ''}
</AppBox>
)
}
export default Column
当然我们定义props字段也可以通过可选?方式定义,如下所示:
type ColumnProps = {
text?: string
}
// 相当于 string | undefined,在我们组件中使用,需要做一层判断
4、如何定义组件 + Children属性
通常react组件我们可以向其内部插入Children去定制我们的组件,props中自带Children属性(使用vue的同学可以更简单的理解他更像是一个slot插槽的概念一样),那么我们如何用ts来去定义这样的props呢?具体如下几种方式:
- 1、通过React.PropsWithChildren
= P & { children?: React.ReactNode }
import { AppBox } from '../styles'
type ColumnProps = {
text: string
}
type PropsInfo = React.PropsWithChildren<ColumnProps>
const Column: React.FC<PropsInfo> = ({ text, children }) => {
return (
<AppBox>
{text || ''}
</AppBox>
)
}
export default Column
- 2、可以通过type方式直接定义,当然我觉得这种方式会更加简单一些,具体如下:
type ColumnProps = {
text: string.
children?: React.ReactNode
}
4、关于useState
通常我们通过如下的方式定义一个state
const [show, setShow] = useState(false)
这样show的类型就会被解读为boolean类型,这种写法比较粗暴,我们解读一下useState的定义方式如下:
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
通过定义,我们可知,useState生成的数据类型我们可以通过泛型的方式去指定具体类型,如果没有指定它将会通过type S的方式自行去推导类型,所以我一直觉得最友好的的写法如下,可能麻烦一点,但是更明确一点
const [show, setShow] = useState<boolean>(false)
5、关于事件类型定义
如果我们想要对一个事件进行类型定义,具体的事件类型定义我们可以参考@types/react/index.d.ts内部, 此处我摘录部分事件的定义结合事件用法说明;
// Focus Events
onFocus?: FocusEventHandler<T> | undefined;
onFocusCapture?: FocusEventHandler<T> | undefined;
onBlur?: FocusEventHandler<T> | undefined;
onBlurCapture?: FocusEventHandler<T> | undefined;
// Form Events
onChange?: FormEventHandler<T> | undefined;
onChangeCapture?: FormEventHandler<T> | undefined;
onBeforeInput?: FormEventHandler<T> | undefined;
onBeforeInputCapture?: FormEventHandler<T> | undefined;
onInput?: FormEventHandler<T> | undefined;
onInputCapture?: FormEventHandler<T> | undefined;
onReset?: FormEventHandler<T> | undefined;
onResetCapture?: FormEventHandler<T> | undefined;
onSubmit?: FormEventHandler<T> | undefined;
onSubmitCapture?: FormEventHandler<T> | undefined;
onInvalid?: FormEventHandler<T> | undefined;
onInvalidCapture?: FormEventHandler<T> | undefined;
在我们实际开发过程中,如果需要在html元素添加事件,我们便可以通过如下的方式定义事件:
// 这块我们定义一个键盘输入事件
const onKeyEnter: React.KeyboardEventHandler<HTMLInputElement> = e => {
if(e.key !== 'Enter') {
return
}
onAdd(text)
}
return (
<NewItemFormContainer>
{/* input元素 */}
<NewItemInput
value={text}
ref={ref}
onChange={(e) => setText(e.target.value)}
onKeyDown={onKeyEnter}
/>
<NewItemButton onClick={() => onAdd(text)}>
create
</NewItemButton>
</NewItemFormContainer>
)
这块我们可以通过React.KeyboardEventHandler这种方式去定义事件,这样我们便可以有良好的提示
这期我们先写这么多,后续我继续从零开始ts项目中将一些类型使用加入进来,项目代码我上传至gitee码云上面,每章我都会打一个tag,仓库地址方便大家参考。