React+Typescript教程:零基础快速入门指南
React+TypeScript教程涵盖了从环境搭建到基本概念与语法的全面介绍。文章详细讲解了如何安装Node.js和npm,使用Create React App初始化项目,以及安装和配置TypeScript。此外,还涉及了React组件、TypeScript类型定义以及高阶组件和Hooks的使用。
React+TypeScript环境搭建 安装Node.js和npm在开始构建React+TypeScript应用之前,需要确保你的计算机上已经安装了Node.js和npm。Node.js是一个JavaScript运行时环境,npm是Node.js的包管理器。你可以从Node.js官方网站下载最新版本的Node.js,安装过程中会自动安装npm。
以下是安装Node.js和npm的一些常用命令:
node -v
npm -v
npm install -g npm
如果你使用的是Linux系统,可以使用以下命令来安装Node.js:
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
使用Create React App初始化项目
Create React App
是一个官方推荐的工具,用于快速搭建React应用。你可以使用它来初始化一个新的React项目,并自动配置TypeScript支持。
安装Create React App
可以通过以下命令安装create-react-app
:
npm install -g create-react-app
初始化一个React项目
在命令行中执行以下命令来初始化一个React项目:
create-react-app my-app --template typescript
my-app
是你的项目的名称。--template typescript
参数表示你希望使用TypeScript模板来初始化项目。
初始化完成后,你可以进入项目目录并运行项目:
cd my-app
npm start
这将启动开发服务器,并在浏览器中打开应用。
安装TypeScript并配置项目在项目初始化之后,TypeScript已经自动配置好了。但为了确保一切正常,你可以检查一下项目中的TypeScript配置文件。
检查TypeScript配置
进入项目目录,你会看到一个tsconfig.json
配置文件。这个文件定义了TypeScript编译器的选项。你可以根据需要调整配置。
确保tsconfig.json
文件的内容如下:
{
"compilerOptions": {
"target": "ES6",
"module": "ESNext",
"strict": true,
"jsx": "react",
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": "src",
"allowJs": true,
"outDir": "./dist",
"rootDir": "./src",
"incremental": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"lib": ["ESNext", "DOM"]
},
"include": ["src/**/*"]
}
安装额外依赖
有时候,你可能需要安装一些额外的依赖,例如@types/react
和@types/react-dom
,这些是React的TypeScript类型定义。
npm install @types/react @types/react-dom --save-dev
运行项目
再次运行项目,确保一切正常:
npm start
基本概念与语法
React组件与State
在React中,组件是应用程序的基本构建块。组件可以被定义为函数或类。类组件可以有状态(state),而函数组件则没有。
类组件
类组件定义如下:
import React, { Component } from 'react';
class HelloMessage extends Component {
state = {
message: 'Hello, TypeScript!'
};
render() {
return <div>{this.state.message}</div>;
}
}
export default HelloMessage;
函数组件
函数组件定义如下:
import React from 'react';
const HelloMessage = () => {
return <div>Hello, TypeScript!</div>;
};
export default HelloMessage;
TypeScript的基本类型与接口
TypeScript是一种静态类型语言,它允许你在编写代码时定义类型。这有助于减少错误并提高代码的可维护性。
基本类型
TypeScript支持多种基本类型,包括:
number
:表示数字string
:表示字符串boolean
:表示布尔值null
和undefined
:表示空值void
:表示无返回值
let age: number = 25;
let name: string = 'John Doe';
let isActive: boolean = true;
let nullValue: null = null;
let undefinedValue: undefined = undefined;
let noReturn: void;
接口(Interface)
接口用于定义对象的结构。它是一种定义对象的类型的方式。
interface User {
id: number;
name: string;
email: string;
}
let user: User = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
};
props与TypeScript类型定义
在React中,组件可以通过props来传递数据。在TypeScript中,你可以为这些props定义类型。
定义props类型
interface Props {
name: string;
age: number;
}
const User = (props: Props) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.age}</p>
</div>
);
};
使用类型别名
你可以使用类型别名来定义props的类型。
type UserProps = {
name: string;
age: number;
};
const User = (props: UserProps) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.age}</p>
</div>
);
};
默认props
你可以为组件定义默认props。
const defaultProps = {
name: 'Guest',
age: 18
};
const User = (props: UserProps) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.age}</p>
</div>
);
};
User.defaultProps = defaultProps;
TypeScript类型推断与类型断言
组件类型推断
TypeScript在处理组件时会自动进行类型推断。例如,当你传递一个对象给组件时,TypeScript会尝试推断这个对象的类型。
const data = {
name: 'John Doe',
age: 30
};
const User = (props: { name: string; age: number }) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.age}</p>
</div>
);
};
<User data={data} />;
在这个例子中,TypeScript会推断data
对象的类型为{ name: string; age: number; }
。
类型断言允许你在编译时强制指定对象的类型。这在你确定某种类型但TypeScript无法推断时非常有用。
const name: any = 'John Doe';
// 类型断言
const nameString: string = name as string;
console.log(nameString); // 输出 'John Doe'
类型断言可以用于组件的props,以确保它们具有正确的类型。
interface UserProps {
name: string;
age: number;
}
const User = (props: UserProps) => {
return (
<div>
<h1>{props.name}</h1>
<p>{props.age}</p>
</div>
);
};
const data = {
name: 'John Doe',
age: 30
};
const userProps = data as UserProps;
<User userProps={userProps} />;
高阶组件与TypeScript
高阶组件介绍
高阶组件(Higher-Order Components,HOC)是一种高级的React技术,用于复用组件中的逻辑。HOC本身是一个函数,它接收一个组件作为输入,并返回一个新的、增强的组件。
简单的HOC示例
import React from 'react';
interface EnhancedComponentProps {
count: number;
}
interface EnhancedComponentState {
count: number;
}
const withCounter = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
class EnhancedComponent extends React.Component<P, EnhancedComponentState> {
state = {
count: 0
};
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return <WrappedComponent {...this.props} count={this.state.count} />;
}
}
return EnhancedComponent;
};
const Greeting = (props: EnhancedComponentProps) => {
return <div>{`Count is ${props.count}`}</div>;
};
const EnhancedGreeting = withCounter(Greeting);
export default EnhancedGreeting;
在这个例子中,withCounter
是一个HOC,它接收一个组件WrappedComponent
,并在外部添加了一个计数器逻辑。EnhancedGreeting
是使用HOC增强后的组件。
要为HOC定义类型,可以使用泛型。这允许你在定义HOC时指定组件的props类型。
import React from 'react';
interface EnhancedComponentProps {
count: number;
}
interface EnhancedComponentState {
count: number;
}
const withCounter = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
class EnhancedComponent extends React.Component<P, EnhancedComponentState> {
state = {
count: 0
};
increment = () => {
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return <WrappedComponent {...this.props} count={this.state.count} />;
}
}
return EnhancedComponent;
};
const Greeting = (props: EnhancedComponentProps) => {
return <div>{`Count is ${props.count}`}</div>;
};
const EnhancedGreeting = withCounter(Greeting);
export default EnhancedGreeting;
通过使用泛型,你可以确保HOC中的EnhancedComponent
组件和WrappedComponent
组件的类型一致。
React Hooks允许你在不编写类的情况下使用React的状态和其他特性。它们为函数组件带来了可复用的功能。
基本Hook
useState
是React中一个常用的Hook,用于在函数组件中添加状态。
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState<number>(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<p>{count}</p>
</div>
);
};
export default Counter;
在这个例子中,useState
返回一个包含当前状态和更新状态函数的数组。count
是当前状态,setCount
是更新状态的函数。
常用Hooks
useState<T>
:添加状态useEffect
:执行副作用操作useContext
:订阅Context的变化useReducer
:处理更复杂的逻辑useCallback
:缓存函数useMemo
:缓存计算结果useRef
:访问DOM节点或直接访问一个引用值
在TypeScript中,你可以为Hooks定义类型,以确保它们的正确使用。
为useState
定义类型
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState<number>(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<p>{count}</p>
</div>
);
};
export default Counter;
在这个例子中,useState
的类型被明确为number
。
为useEffect
定义类型
useEffect
通常用于副作用操作,例如数据获取、订阅和手动DOM操作。
import React, { useState, useEffect } from 'react';
const Counter = () => {
const [count, setCount] = useState<number>(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<p>{count}</p>
</div>
);
};
export default Counter;
在这个例子中,useEffect
的依赖数组被指定为[count]
,确保只有count
变化时才会触发副作用。
现在我们来构建一个简单的Todo应用,使用TypeScript和React。这个应用将允许用户添加、编辑和删除待办事项。
项目结构
项目的基本结构如下:
my-todo-app/
├── public/
│ └── index.html
├── src/
│ ├── App.tsx
│ ├── index.tsx
│ ├── services/
│ │ └── todoService.ts
│ ├── components/
│ │ ├── TodoItem.tsx
│ │ └── TodoForm.tsx
│ ├── types/
│ │ └── TodoType.ts
├── tsconfig.json
└── package.json
Todo类型定义
首先,定义一个Todo
类型。
// src/types/TodoType.ts
export interface Todo {
id: number;
text: string;
isCompleted: boolean;
}
服务层
创建一个服务层来处理Todo的逻辑。
// src/services/todoService.ts
import { Todo } from '../types/TodoType';
let todos: Todo[] = [];
const addTodo = (text: string) => {
const id = todos.length ? todos[todos.length - 1].id + 1 : 1;
todos.push({ id, text, isCompleted: false });
};
const deleteTodo = (id: number) => {
todos = todos.filter(todo => todo.id !== id);
};
const toggleTodo = (id: number) => {
const todo = todos.find(todo => todo.id === id);
if (todo) {
todo.isTodo.isCompleted = !todo.isCompleted;
}
};
export { addTodo, deleteTodo, toggleTodo };
TodoItem组件
创建一个TodoItem
组件来显示单个待办事项。
// src/components/TodoItem.tsx
import React from 'react';
import { Todo } from '../../types/TodoType';
import { toggleTodo, deleteTodo } from '../../services/todoService';
interface TodoItemProps {
todo: Todo;
}
const TodoItem: React.FC<TodoItemProps> = ({ todo }) => {
const toggle = () => {
toggleTodo(todo.id);
};
const remove = () => {
deleteTodo(todo.id);
};
return (
<li>
<input type="checkbox" checked={todo.isCompleted} onChange={toggle} />
<span>{todo.text}</span>
<button onClick={remove}>Remove</button>
</li>
);
};
export default TodoItem;
TodoForm组件
创建一个TodoForm
组件来添加新的待办事项。
// src/components/TodoForm.tsx
import React, { useState } from 'react';
import { addTodo } from '../../services/todoService';
const TodoForm: React.FC = () => {
const [text, setText] = useState('');
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
addTodo(text);
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={event => setText(event.target.value)}
/>
<button type="submit">Add Todo</button>
</form>
);
};
export default TodoForm;
App组件
最后,创建一个App
组件来组装整个待办事项应用。
// src/App.tsx
import React, { useEffect, useState } from 'react';
import './App.css';
import { Todo } from '../types/TodoType';
import TodoForm from '../components/TodoForm';
import TodoItem from '../components/TodoItem';
import { addTodo, deleteTodo, toggleTodo } from '../services/todoService';
const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>(todos);
useEffect(() => {
setTodos(todos);
}, [todos]);
const toggleTodo = (id: number) => {
setTodos(prevTodos => {
return prevTodos.map(todo => {
if (todo.id === id) {
todo.isCompleted = !todo.isCompleted;
}
return todo;
});
});
};
const deleteTodo = (id: number) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
};
return (
<div className="App">
<h1>Todo App</h1>
<TodoForm />
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
</div>
);
};
export default App;
index.tsx
确保index.tsx
文件正确导入并渲染App
组件。
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals();
public/index.html
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
reportWebVitals.ts
展示reportWebVitals
函数的具体实现:
// src/reportWebVitals.ts
import { WebVitals } from '@firebase/analytics/dist/esm/types';
import { useEffect } from 'react';
const reportWebVitals = (onPerfEntry?: (entry: WebVitals) => void) => {
if (onPerfEntry === undefined) {
return;
}
const timer = Date.now();
const handle = (entry: WebVitals) => {
onPerfEntry(entry);
window.performance.mark(`${entry.name}-${timer}`);
};
useEffect(() => {
const handleTimeOrigin = (performance: Performance) => {
performance.getEntriesByType('navigation').forEach(entry => {
handle({
id: entry.name,
label: entry.name,
name: entry.name,
duration: entry.responseEnd - entry.startTime,
startTime: entry.startTime,
type: 'navigation',
id: entry.name,
});
});
performance.getEntriesByType('paint').forEach(entry => {
handle({
id: entry.name,
label: entry.name,
name: entry.name,
duration: entry.startTime - timer,
startTime: entry.startTime,
type: 'paint',
id: entry.name,
});
});
};
const performance = window.performance;
if (performance) {
handleTimeOrigin(performance);
}
}, []);
};
export default reportWebVitals;
``
通过以上步骤,你可以构建一个简单的Todo应用,使用TypeScript和React来定义类型和组件。这个示例展示了如何使用TypeScript来增强React应用的类型安全性,并确保代码的清晰和可维护性。
共同学习,写下你的评论
评论加载中...
作者其他优质文章