阅读视图

发现新文章,点击刷新页面。

从原生 JS 到 React:手把手带你开启 React 业务开发之旅

一、前端开发演进:从切图崽到全干工程师的成长路径

在开始React业务开发之前,让我们先了解前端工程师的成长路线:

  1. 前端切图崽:掌握HTML+CSS+基础JS,能实现静态页面
  2. 前端开发工程师:掌握Vue/React等框架,能开发复杂Web应用
  3. 全栈开发工程师:掌握Node.js+数据库,实现前后端一体化
  4. 跨平台应用开发:掌握React Native等,开发Android/iOS应用
  5. AI应用开发:整合AI能力,开发智能化应用
  6. 全干工程师:掌握全技术栈,独立完成项目全流程

React作为现代前端开发的三大框架之一,让开发者得以脱离底层 DOM 操作,聚焦于业务逻辑,是我们进阶路上的重要里程碑。

二、React 项目初始化:从 vite 模板开始

1. 核心工具:npm 与 vite 的作用

  • npm(Node Package Manager) :作为 Node.js 的包管理器,负责安装 React 等开发依赖,如通过npm install react react-dom引入核心库。
  • vite:新一代前端构建工具,相比传统 webpack,具有极速冷启动、按需编译的特点,尤其适合 React 项目的工程化搭建。

2. 快速创建项目的 4 步流程

# 1. 初始化vite项目(选择react模板语言选择js)
npm init vite@latest my-react-app -- --template react

# 2. 进入项目目录
cd my-react-app

# 3. 安装依赖
npm install

# 4. 启动开发服务器(默认端口5173)
npm run dev

执行完成后,浏览器访问http://localhost:5173即可看到 React 欢迎页面

image.png

项目结构如下:

image.png

三、React 初体验:组件化开发的核心逻辑

1. 组件:HTML+CSS+JS 的封装单元

React 组件是完成开发任务的最小单元,它将 HTML、CSS、JS 逻辑封装在一个函数中,通过 组件组合 构建完整页面。以下是一个基础的 App 组件示例,演示如何通过函数组件渲染静态数据:

import { useState } from 'react';
import './App.css';

// 函数组件:接收参数并返回 JSX 结构
function App() {
  const staticTodos = ['吃饭', '睡觉', '打豆豆']; 
  return (
    <>
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>任务</th>
          </tr>
        </thead>
        <tbody>
          {/* 在 JSX 中通过 {} 嵌入 JS 表达式,使用 map 遍历渲染列表 */}
          {staticTodos.map((item, index) => (
            <tr key={index}> {/* 列表渲染必须指定唯一 key,此处仅为演示,实际应使用数据唯一标识 */}
              <td>{index + 1}</td>
              <td>{item}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  );
}

export default App;

image.png

关键知识点:

  • JSX 语法:允许在 JS 中直接书写类 HTML 结构,{} 用于插入 JS 表达式(如变量、函数调用等)。
  • 列表渲染:通过 Array.map() 遍历数据并生成多个 JSX 元素,需为每个列表项指定唯一 key(React 用于追踪组件更新的标识)。
  • 组件结构:函数组件必须有一个根返回值(如 <> 空标签或 <div> 等容器元素)。

2. 响应式数据:useState Hook 实现数据驱动视图

当数据需要动态变更并触发视图更新时,需使用 React 的 状态(State)  机制。通过 useState Hook 声明响应式数据,数据变更时 React 会自动重新渲染组件。

示例:动态更新标题与任务列表

import { useState } from 'react';
import './App.css';

function App() {
  // 声明响应式状态:todos(任务列表)和 title(页面标题)
  const [todos, setTodos] = useState(['吃饭', '睡觉', '打豆豆']);
  const [title, setTitle] = useState('今天你吃饭了吗'); 

  // 模拟异步操作:5 秒后更新
  setTimeout(() => {
    setTitle('今天你完成任务了吗');
    setTodos([...todos, '养鱼']); 
  }, 5000);

  return (
    <div>
      <h1 className="title">{title}</h1> {/* 绑定响应式数据 title */}
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>任务</th>
          </tr>
        </thead>
        <tbody>
          {todos.map((item, index) => (
            <tr key={index}>
              <td>{index + 1}</td>
              <td>{item}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default App;

CSS 样式(App.css):

* {
  margin: 0;
  padding: 0;
}

.title {
  background-color: aqua;
  color: aliceblue;
  padding: 10px;
  margin-bottom: 20px;
}

image.png

五秒后更新为: image.png

核心概念解析:

  • useState 用法

    • 调用 useState(initialValue) 返回一个数组,第一项是状态值(如 todos),第二项是更新状态的函数(如 setTodos)。
    • 状态更新函数(如 setTitle)的调用会触发组件重新渲染,确保视图与数据同步。
  • 异步更新:即使在定时器、Promise 等异步场景中调用 setState,React 也能正确捕获变更并更新视图。

  • 数据不可变性:更新数组或对象状态时,需通过 [...todos] 或 {...obj} 创建新对象,避免直接修改原数据导致 React 无法检测变更。

3. 组件组合:拆解复杂页面为可复用单元

React 推荐将页面拆分为多个小组件,通过嵌套组合实现复杂功能。以下是对示例代码的扩展思路 :

// 拆分标题组件
function Title({ title }) {
  return <h1 className="title">{title}</h1>;
}

// 拆分任务列表组件
function TodoTable({ todos }) {
  return (
    <table>
      <thead>
        <tr>
          <th>序号</th>
          <th>任务</th>
        </tr>
      </thead>
      <tbody>
        {todos.map((item, index) => (
          <tr key={index}>
            <td>{index + 1}</td>
            <td>{item}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

function App() {
  const [todos, setTodos] = useState(['吃饭', '睡觉', '打豆豆']);
  const [title, setTitle] = useState('今天你吃饭了吗');

  setTimeout(() => setTitle('今天你完成任务了吗'), 5000);

  return (
    <div>
      <Title title={title} /> {/* 父组件向子组件传递数据 via props */}
      <TodoTable todos={todos} /> {/* 复用列表组件,传入不同数据 */}
    </div>
  );
}

组件通信规则:

  • 父传子:通过 props 向子组件传递数据(如 Title 组件的 title 属性)。
  • 子传父:子组件通过回调函数(如 onClick={handleClick})向父组件传递事件。
  • 跨组件通信:状态提升(将共享状态提升至共同父组件)或使用 Context 实现全局状态管理。

4. 对比原生 JS:React 如何简化开发

场景 原生 JS 实现方式 React 实现方式
渲染列表 使用 createElement 或 innerHTML 拼接 DOM 字符串 通过 JSX + map 直接声明视图结构
数据更新 手动查找 DOM 节点并更新 textContent/innerHTML 调用 setState 自动触发视图更新
组件复用 复制粘贴代码或封装函数返回 DOM 节点 定义函数组件并通过 props 传递差异化数据

核心优势:React 通过声明式编程(描述 “是什么” 而非 “怎么做”)和组件化架构,让开发者聚焦业务逻辑而非底层 DOM 操作,极大提升开发效率与代码可维护性。

四、实战案例:构建响应式待办事项应用

1. 功能拆解

我们将通过实际代码实现一个包含以下功能的待办应用:

  • 输入框添加新任务(表单组件TodoForm
  • 任务列表展示(列表组件Todos
  • 数据持久化(暂存于组件状态,后续可扩展 localStorage)

2. 核心组件实现

首先我们先在src目录下创建一个components文件夹,存放各个组件

(1)表单组件TodoForm:双向绑定与提交处理

import { useState } from 'react';

function TodoForm(props) {
  // 从 props 中获取父组件传递的回调函数(用于接收新增任务)
  const onAdd = props.onAdd;
  // 声明本地状态:text 存储输入框内容,初始值为"打豆豆"
  const [text, setText] = useState('打豆豆');

  // 表单提交处理函数
  const handleSubmit = (e) => {
    // 阻止表单默认提交行为(避免页面跳转)
    e.preventDefault();
    // 调用父组件回调,传递当前输入的文本
    onAdd(text);
    console.log(e.target.value);
  };

  // 输入框内容变化处理函数
  const handleChange = (e) => {
    // 更新本地状态,实现双向绑定(输入框值与 state 同步)
    setText(e.target.value);
  };

  return (
    <form action="http://www.baidu.com" onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="请输入待办事项"
        value={text}          // 绑定 state 到输入框 value
        onChange={handleChange} // 监听输入变化并更新 state
      />
      <button type="submit">添加</button>
    </form>
  );
}

export default TodoForm;

在这个组件中,通过useState定义text状态,实现输入框的双向绑定。handleChange函数监听输入变化更新状态,handleSubmit函数阻止表单默认提交,并调用父组件传递的onAdd回调函数,将输入内容传递出去。

(2)列表组件Todos:数据渲染与 key 的重要性

function Todos(props) {
  console.log(props, '/////');
  // 解构 props 中的 todos 数据
  const todos = props.todos;

  return (
    <ul>
      {
        // 遍历 todos 数组,渲染为列表项
        todos.map((todo) => (
          // key 是 React 识别列表项的唯一标识,必须使用稳定的唯一值(如数据 ID)
          <li key={todo.id}>{todo.text}</li>
        ))
      }
    </ul>
  );
}

export default Todos;

该组件接收父组件传递的props.todos数据,使用map方法遍历数据并渲染为列表项。为每个列表项设置唯一的key属性(这里使用todo.id),这能帮助 React 高效地更新列表,避免在数据变化时出现渲染错误。

(3)核心逻辑组件TodoList:状态管理与组件组合

import { useState } from 'react';
import '../Todo.css'; // 引入组件级样式(需确保路径正确)
import TodoForm from './TodoForm'; // 引入表单组件(子组件)
import Todos from './Todos'; // 引入列表组件(子组件)

function TodoList() {
  const [hi, setHi] = useState('haha');
  const [title, setTitle] = useState('Todo List');
  const [todos, setTodos] = useState([
    {
      id: 1,          // 唯一标识(必填,用于列表渲染和更新)
      text: '吃饭',  
      completed: false 
    }
  ]);

  // 添加任务的回调函数(供子组件调用)
  const handleAdd = (text) => {
    // 更新 todos 状态:使用展开运算符保留原有数据,新增任务项
    setTodos([
      ...todos,
      {
        id: todos.length + 1, 
        text,                // 解构参数作为任务文本
        completed: false      
      }
    ]);
  };

  return (
    <div className="container">
      <h1 className="title">
        {title}{hi} {/* 组合显示标题和演示文本 */}
      </h1>
      <TodoForm onAdd={handleAdd} /> {/* 向子组件传递回调函数,实现子传父通信 */}
      <Todos todos={todos} />       {/* 向子组件传递数据,实现父传子通信 */}
    </div>
  );
}

export default TodoList;

TodoList组件作为整个待办应用的核心,通过useState定义了多个状态,如titletodoshandleAdd函数用于处理添加任务的逻辑,当调用setTodos更新任务列表状态时,React 会自动重新渲染包含Todos组件的部分,实现数据驱动视图更新。同时,通过propshandleAdd函数传递给TodoForm组件,完成子传父的通信。

(4)根组件App:整合应用

import { useState } from 'react'
import './App.css'
import TodoList from './components/TodoList'

function App() {
  return (
    <>
      <div>
      </div> 
 {/* 渲染待办事项核心组件 */}
      <TodoList />
    </>
  )
}

export default App

App组件作为应用的入口,只负责引入并渲染TodoList组件,将整个待办应用集成到项目中。

image.png

通过这个完整的待办事项应用案例,我们能更清晰地看到 React 中组件化开发、数据驱动视图、组件间通信等核心概念的实际应用。后续还可以在此基础上,添加任务删除、任务状态切换等功能,进一步深化对 React 开发的理解 。

学习 React 之路道阻且长,唯有持续深耕

从原生 JS 到 React,前端开发变得更加高效和便捷。React 的组件化开发和响应式数据管理,让开发者能够聚焦于业务逻辑,而无需过多关注底层的 DOM 操作。随着技术的不断发展,相信前端开发还会迎来更多的变革和创新。

❌