数字化技能提升教程,数字化时代生存宝典

React教程:从项目搭建到任务管理应用

浏览:797

React 是一个用于构建用户界面的 JavaScript 库,适合开发后台系统、管理面板、内容网站和交互式Web应用。本教程将从环境准备开始,依次介绍 JSX、组件、Props、状态、Hooks、表单、路由和 API 请求,最后完成一个可以部署上线的任务管理应用。

如果需要将React项目部署到公网,可以使用具备独立资源和管理员权限的 Hostinger VPS,在服务器上安装Node.js、构建项目并通过Nginx提供访问。

Hostinger官网:点击直达

一、准备React开发环境

学习本教程前,建议了解基础的HTML、CSS和JavaScript,尤其是变量、函数、数组、对象、解构赋值、模块以及 map() 和 filter() 等数组方法。

React项目需要使用Node.js和npm。Node.js用于运行Vite等开发工具,npm则用于安装和管理项目依赖。

安装Node.js后,在终端检查版本:

node --version
npm --version

如果两个命令都返回版本号,说明环境已经准备完成。

二、使用Vite创建React项目

本教程使用Vite创建项目。Vite启动速度快,配置简单,适合新建React应用。

执行以下命令:

npm create vite@latest react-task-tracker -- --template react
cd react-task-tracker
npm install
npm run dev

其中:

  • npm create vite@latest 用于创建Vite项目。
  • --template react 表示使用React模板。
  • npm install 安装项目依赖。
  • npm run dev 启动本地开发服务器。

启动成功后,终端通常会显示类似以下地址:

http://localhost:5173

在浏览器中打开该地址,即可查看React初始页面。

三、了解React项目结构

创建项目后,主要文件通常位于 src 目录中:

react-task-tracker/
├── package.json
├── index.html
└── src/
    ├── main.jsx
    ├── App.jsx
    ├── App.css
    └── index.css

各文件的作用如下:

  • package.json:记录项目依赖和运行命令。
  • index.html:提供React应用挂载的HTML页面。
  • src/main.jsx:启动React应用并加载根组件。
  • src/App.jsx:保存主要应用组件。
  • src/App.css:保存应用相关样式。
  • src/index.css:保存全局样式。

后续可以在 src 目录中创建:

src/components
src/pages

components 用于保存可复用组件,pages 用于保存不同页面组件。

文件名称的大小写需要保持一致。例如,App.jsx 和 app.jsx 在部分系统中会被视为两个不同的文件。

四、理解JSX

JSX是React使用的一种JavaScript语法扩展,可以在JavaScript代码中编写类似HTML的结构。

打开 src/App.jsx,替换为:

const taskName = '学习 JSX';

function App() {
return (
<main className="task-list">
<h1>任务管理器</h1>
<p>{taskName}</p>
</main>
);
}

export default App;

JSX与HTML有一些区别:

  • 多个元素需要放在同一个父元素或片段中。
  • 所有标签都必须闭合。
  • 使用 className 代替HTML中的 class。
  • 事件属性通常使用驼峰形式,例如 onClick。
  • JavaScript表达式需要放在 {} 中。

例如:

<p>{taskName}</p>

这里的 {taskName} 会显示变量中的内容。

保存文件后,浏览器页面会自动更新。可以修改任务名称,再增加一个状态文本:

<p>未开始</p>

五、创建React组件

React组件通常是返回JSX的JavaScript函数。将页面拆分成多个组件后,代码更容易维护。

先创建目录:

mkdir src/components

在 src/components 中创建 Header.jsx:

function Header() {
  return (
    <header className="header">
      <h1>任务管理器</h1>
    </header>
  );
}

export default Header;

然后修改 src/App.jsx:

import Header from './components/Header.jsx';

function App() {
return (
<main className="app">
<Header />
</main>
);
}

export default App;

export default Header 允许其他文件导入该组件。<Header /> 则表示在页面中使用Header组件。

六、使用Props传递数据

Props用于父组件向子组件传递数据。

创建 src/components/TaskItem.jsx:

function TaskItem({ title, done }) {
  return (
    <li className="task-item">
      {done ? '✓ ' : '○ '}
      {title}
    </li>
  );
}

export default TaskItem;

这里的 title 和 done 就是组件接收的Props。

修改 src/App.jsx:

import Header from './components/Header.jsx';
import TaskItem from './components/TaskItem.jsx';

function App() {
return (
<main className="app">
<Header />

<ul>
<TaskItem title="学习 JSX" done={true} />
<TaskItem title="创建React组件" done={false} />
</ul>
</main>
);
}

export default App;

两个任务接收了不同的 done 值,因此页面会显示不同的完成状态。

Props是只读数据,子组件可以使用父组件传入的值,但不应该直接修改它。

七、渲染任务列表

接下来创建 TaskList.jsx,用于循环显示多个任务。

import TaskItem from './TaskItem.jsx';

function TaskList({ tasks }) {
if (tasks.length === 0) {
return <p>当前没有任务。</p>;
}

return (
<ul className="task-list">
{tasks.map((task) => (
<TaskItem
key={task.id}
title={task.title}
done={task.done}
/>
))}
</ul>
);
}

export default TaskList;

map() 会为数组中的每个任务生成一个 TaskItem。

key 用于帮助React识别列表中的每一项。每个任务都应使用稳定且唯一的值作为 key。

修改 src/App.jsx:

import Header from './components/Header.jsx';
import TaskList from './components/TaskList.jsx';

const tasks = [
{ id: 1, title: '学习 JSX', done: true },
{ id: 2, title: '创建React组件', done: false },
];

function App() {
return (
<>
<Header />

<main className="app">
<TaskList tasks={tasks} />
</main>
</>
);
}

export default App;

现在,页面结构已经拆分为Header、TaskList和TaskItem三个组件。

八、使用State实现交互

如果任务状态需要在页面操作后发生变化,就需要使用State。

修改 src/App.jsx:

import { useState } from 'react';
import Header from './components/Header.jsx';
import TaskList from './components/TaskList.jsx';

function App() {
const [tasks, setTasks] = useState([
{ id: 1, title: '学习 JSX', done: false },
{ id: 2, title: '创建React组件', done: false },
]);

function toggleTask(taskId) {
setTasks((currentTasks) =>
currentTasks.map((task) =>
task.id === taskId
? { ...task, done: !task.done }
: task
)
);
}

return (
<>
<Header />

<main className="app">
<TaskList
tasks={tasks}
onToggleTask={toggleTask}
/>
</main>
</>
);
}

export default App;

然后修改 TaskList.jsx:

import TaskItem from './TaskItem.jsx';

function TaskList({ tasks, onToggleTask }) {
if (tasks.length === 0) {
return <p>当前没有任务。</p>;
}

return (
<ul className="task-list">
{tasks.map((task) => (
<TaskItem
key={task.id}
title={task.title}
done={task.done}
onToggle={() => onToggleTask(task.id)}
/>
))}
</ul>
);
}

export default TaskList;

最后修改 TaskItem.jsx:

function TaskItem({ title, done, onToggle }) {
  return (
    <li className="task-item">
      <button type="button" onClick={onToggle}>
        {done ? '✓' : '○'}
      </button>

<span>{title}</span>
</li>
);
}

export default TaskItem;

点击任务前面的按钮后,父组件中的State会更新,React随后重新渲染任务列表。

九、添加任务表单

创建 src/components/TaskForm.jsx:

import { useState } from 'react';

function TaskForm({ onAddTask }) {
const [title, setTitle] = useState('');

function handleSubmit(event) {
event.preventDefault();

const trimmedTitle = title.trim();

if (!trimmedTitle) {
return;
}

onAddTask(trimmedTitle);
setTitle('');
}

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(event) => setTitle(event.target.value)}
placeholder="输入任务名称"
/>

<button type="submit">添加任务</button>
</form>
);
}

export default TaskForm;

在 App.jsx 中增加添加任务逻辑:

import { useState } from 'react';
import Header from './components/Header.jsx';
import TaskForm from './components/TaskForm.jsx';
import TaskList from './components/TaskList.jsx';

function App() {
const [tasks, setTasks] = useState([
{ id: 1, title: '学习 JSX', done: false },
{ id: 2, title: '创建React组件', done: false },
]);

function addTask(title) {
setTasks((currentTasks) => [
...currentTasks,
{
id: Date.now(),
title,
done: false,
},
]);
}

function toggleTask(taskId) {
setTasks((currentTasks) =>
currentTasks.map((task) =>
task.id === taskId
? { ...task, done: !task.done }
: task
)
);
}

return (
<>
<Header />

<main className="app">
<TaskForm onAddTask={addTask} />

<TaskList
tasks={tasks}
onToggleTask={toggleTask}
/>
</main>
</>
);
}

export default App;

这个表单属于受控组件,输入框的值由React State管理。提交时,程序会先阻止页面刷新,再检查任务名称是否为空,最后将新任务添加到列表中。

十、使用useEffect获取API数据

useEffect适合处理组件渲染之外的操作,例如获取接口数据、监听事件和更新浏览器标题。

import { useEffect, useState } from 'react';

function RemoteTasks() {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');

useEffect(() => {
async function loadTasks() {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/todos?_limit=5'
);

if (!response.ok) {
throw new Error('请求失败');
}

const data = await response.json();

setTasks(
data.map((item) => ({
id: item.id,
title: item.title,
done: item.completed,
}))
);
} catch (requestError) {
setError(requestError.message);
} finally {
setLoading(false);
}
}

loadTasks();
}, []);

if (loading) {
return <p>正在加载任务...</p>;
}

if (error) {
return <p>任务加载失败:{error}</p>;
}

return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.title}</li>
))}
</ul>
);
}

export default RemoteTasks;

空数组 [] 表示组件首次加载时执行一次请求。

实际项目中,还应处理请求取消、接口鉴权、分页和数据缓存等问题。

十一、使用React Router创建多个页面

安装React Router:

npm install react-router-dom

创建页面目录:

mkdir src/pages

创建 src/pages/HomePage.jsx:

function HomePage() {
  return <h2>任务首页</h2>;
}

export default HomePage;

创建 src/pages/AboutPage.jsx:

function AboutPage() {
  return <h2>关于任务管理器</h2>;
}

export default AboutPage;

然后修改 src/main.jsx:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import {
  BrowserRouter,
} from 'react-router-dom';
import './index.css';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);

在 App.jsx 中配置路由:

import {
  Link,
  Route,
  Routes,
} from 'react-router-dom';
import HomePage from './pages/HomePage.jsx';
import AboutPage from './pages/AboutPage.jsx';

function App() {
return (
<>
<nav>
<Link to="/">首页</Link>
<Link to="/about">关于</Link>
</nav>

<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</Routes>
</>
);
}

export default App;

开发服务器运行时,可以访问:

http://localhost:5173/
http://localhost:5173/about

部署到Nginx时,需要将未知路径回退到 index.html,否则直接刷新子页面可能出现404。

十二、构建React生产版本

开发完成后,在项目目录执行:

npm run build

Vite会将生产文件输出到:

dist/

可以在本地预览生产版本:

npm run preview

部署前建议检查:

  • 页面刷新是否正常。
  • 路由跳转是否正常。
  • API地址是否使用生产环境配置。
  • 图片和静态文件路径是否正确。
  • 移动端页面是否能够正常显示。
  • 生产环境是否启用HTTPS。

十三、使用Hostinger VPS部署React项目

如果需要把React任务管理器发布到公网,可以使用 Hostinger VPS 部署Node.js环境、Nginx和项目文件。

基本流程如下:

  1. 准备一台Linux VPS。
  2. 通过SSH登录服务器。
  3. 安装Node.js和npm。
  4. 上传React项目代码。
  5. 执行 npm install。
  6. 执行 npm run build。
  7. 将 dist目录配置为Nginx站点根目录。
  8. 配置HTTPS和域名解析。
  9. 设置React Router的历史路径回退规则。
  10. 通过浏览器访问正式域名。

如果应用需要运行Node.js后端,还需要使用进程管理工具保持服务运行,并通过Nginx反向代理后端接口。

Hostinger VPS适合部署React静态网站、Node.js应用、API服务和开发测试环境。选择配置时,应根据项目访问量、构建任务和后端服务数量预留CPU、内存及磁盘空间。

十四、常见问题

React项目一定要使用Vite吗?

不一定,但Vite提供了较简洁的开发环境和较快的启动速度,适合新项目使用。

JSX是不是HTML?

JSX看起来类似HTML,但它最终会转换为JavaScript,并遵循React自己的语法规则。

Props和State有什么区别?

Props由父组件传给子组件,主要用于传递数据;State由组件自己管理,可以随着用户操作发生变化。

为什么列表需要key?

key帮助React识别列表中的每一项。使用稳定且唯一的值,可以减少列表更新时的渲染问题。

React项目可以部署到VPS吗?

可以。完成 npm run build 后,React项目通常会生成静态文件,可以通过Nginx部署到Hostinger VPS等Linux服务器上。

React Router部署后刷新页面出现404怎么办?

需要在Web服务器中配置路径回退,让未匹配的前端路径返回 index.html,再由React Router处理页面显示。

相关文章
猜你喜欢