《React入门指南:从零到一掌握核心概念与实战技巧》

162 阅读15分钟

✨前言

作者最近准备更新一个React知识点系列,从零到一的给大家讲解React的一些重难点,由基础到深入。本文为第一篇,后续我会持续更新,相信看完这个系列,你就能成为React的大神!


🚀初始化React

首先讲解如何从零开始初始化一个React的项目。

我们首先使用任何一款编辑器(建议使用Trae,Webstrom,VScode)创建一个新文件夹,用于存放我们的项目。这里作者就使用Trae创建一个react文件夹,我们接下来使用npm这个包管理器帮我们全程创建react项目。

1. npm init vite

我们进入到react的终端目录下,使用npm init vite指令初始化,vite是vue/react项目初始化必不可少的项目模板,有了它,能帮我们做项目的工程化管理,它的核心优势包括快速启动、热更新、优化构建等。

注意这里如果出现一些报错,可以尝试用管理员运行,重新打开即可。

2. 选择一些配置

此时会让我们输入该项目的名称以及选择很多配置:我们输入自己的项目的名称即可,我这里输入的是todoList,然后依次选择react和JavaScript配置即可,此时你会发现出现了许许多多的文件,这是vite帮我们创建的初始化文件。

3. node install

进入到刚刚我们初始化的项目文件夹todoList内,进入到终端,然后执行node install命令,这个操作是目的是帮我们安装依赖,把一些项目的核心从第三方库安装到本地项目之中。注意此时我们的todoList文件夹内出现了node_modules,这是依赖包的目录,说明我们依赖已经安装成功了!

4. npm run dev

在刚刚我们就已经初始化成功了我们的项目,我们此时仍然进入todoList的终端,使用npm run dev运行一下,看看是否能成功吧!


🧩组件化开发

在react中,最核心的特点就是组件式开发!

在react推出来之前,我们前端开发可能是以一个标签作为最小单元,这往往会带来分工上的复杂和一些重复的代码工作,于是react推出了组件式开发,React应用程序就是由组件构成的,此时组件作为了最小开发单元,大大地提升了开发效率以及复用性。


组件就是一个函数

在react中,组件是返回标签的 JavaScript 函数,一个函数代表一个组件。

function App() {
  const 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>
  );
}

在函数体中我们会在return前申明数据和业务逻辑,在return的部分返回各式各样的标签

你会发现函数里的很多内容比较奇怪,比如return 返回一个像HTML的语法的标签集合,map返回的是(...),这种语法被称为JSX,下文会详细讲解JSX的语法。

下面给出react中文文档中提示的陷阱: 组件的名称必须以大写字母开头,否则它们将无法运行!


JSX语法

JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。虽然还有其它方式可以编写组件,但大部分 React 开发者更喜欢 JSX 的简洁性,并且在大部分代码库中使用它。

1. 只能返回一个根元素
如果想要在一个组件中包含多个元素,需要用一个父标签将它们包裹起来。

你可以使用一个<div>标签:

  <div>
  <h1>海蒂·拉玛的待办事项</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    class="photo"
  >
  <ul>
    ...
  </ul>
</div>

如果你不想在标签中增加一个额外的 <div>,可以用 <></> 元素来代替:

<>
  <h1>海蒂·拉玛的待办事项</h1>
  <img 
    src="https://i.imgur.com/yXOvdOSs.jpg" 
    alt="Hedy Lamarr" 
    class="photo"
  >
  <ul>
    ...
  </ul>
</>

2.类名使用 className
因为 class 是 JavaScript 保留字,所以我们在HTML中可以用class的地方在JSX中并不支持:

<div class="app">Content</div>  //不正确
const element = <div className="app">Content</div>;  //正确

3. 自闭合标签
注意在一些HTML中这样的自闭合标签,在JSX中一定要闭合。JSX要求标签必须闭合!

const element = <img src="image.jpg" alt="Example" />;

4.JSX的return规则
返回语句可以全写在一行上,如下面组件中所示:

//在一行则无需()
return <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />;

但是,如果你的标签和 return 关键字不在同一行,则必须把它包裹在一对()中。

function App() {
  const todos = ['吃饭','睡觉','打豆豆'];
  //标签和return不在一行,故需要()包围
  return (
    <>
      <table>
       ...
      </table>
    </>
  )
}

总结:在组件式开发推出来之后,你的leader分配给你的任务也许就是完成某个组件,而你写的这个组件很多情况下是会被很多很多人复用的,你看,是不是既容易分配任务,又大大地减少了代码的重复书写,使得我们能够将更多的时间和精力投入到实现业务中,这就是组件化开发带给我们的意义!



📦模块化导入/导出

了解了组件化开发之后,我们需要知道组件之间有什么关系,在React中,一个组件可以使用导入另一个组件,这个组件也可以被导出,在现代前端开发中,模块化是构建可维护、可扩展应用的关键。React 完全支持 ES6 的模块系统,让组件的导入导出变得清晰而高效。下面详细介绍 React 组件的模块化实践。


1. 默认导出 (Default Export)

每个文件可以有一个默认导出,通常用于导出主要组件:

// Button.js
import React from 'react';

const Button = ({ children }) => {
  return <button className="btn">{children}</button>;
};

export default Button; // 默认导出

注意,默认导出在一个文件中最多只有一个!

使用默认导出时,在导入时可以使用任意名称:

// App.js
import MyButton from './Button'; // 导入时名称可以自定义

function App() {
  return <MyButton>Click Me</MyButton>;
}

2. 命名导出 (Named Export)

一个文件可以使用多个命名导出用来导出组件:

// components.js
export const PrimaryButton = ({ children }) => (
  <button className="btn-primary">{children}</button>
);

export const SecondaryButton = ({ children }) => (
  <button className="btn-secondary">{children}</button>
);

使用命名导出时,在导入时需要指定具体名称:

import { PrimaryButton, SecondaryButton } from './components';

function App() {
  return (
    <>
      <PrimaryButton>主要按钮</PrimaryButton>
      <SecondaryButton>次要按钮</SecondaryButton>
    </>
  );
}


📂开发目录介绍

在上面我们也了解到了,可以通过ES6模块化导入和导出的方式来进行组件的相互使用。

在实战中,我们的相关的一个或一些组件往往放在一个文件中,可以从一个文件中导入和导出多个组件。

而我们这些文件也会放在响应的开发目录中,不同目录文件夹下对应着不同功能的文件,每个文件里面包含着一类相关的组件,我们需要将文件,也就是组件,分好类,放在不同的文件夹下,这样的规范对我们的实战开发来说十分重要!下面就给大家介绍一些最佳实战的分类规范。

image.png

todoList是我们的项目目录,代表着整个项目,而src是我们的源代码目录,也是我们的开发目录,我们对一些组件的创建都是在src目录下的,因此我们着重讲解src目录。

image.png

其中:

  • assets 是用来存放静态资源的
  • components就是用来存放通用组件的,我们一般组件的创建就是在其中进行
  • css 是用来存放我们组件中引用的样式
  • App.jsx就是应用根组件
  • App.css是应用根组件的样式
  • index.css是全局样式
  • main.jsx是应用组件入口

我们一般在默认的src下会创建以下几个文件夹:

  • assets/ :存放静态资源,如图片、字体等
  • components/ :可复用的UI组件
  • pages/ :页面级组件,通常与路由对应
  • hooks/ :自定义Hook,实现逻辑复用
  • store/ :状态管理相关文件
  • services/ :API请求和服务层

这样子创建了之后,就能做到划分职责,每个文件各司其职,这是我们的规范化!



⚡响应式数据

在使用React进行开发时,我们往往不会写完全静态的页面,我们会在页面中添加一些动态数据,这就要使用我们React中的useState实现响应式动态绑定数据了。

UseState

useState是React中的一个非常重要的API,它可以帮我们实现在页面中绑定动态数据,并实现修改。我们想要学会React中的响应式数据,首先就是必须学会useState的使用

1. 基本语法

import { useState } from 'react';  //使用前需先导入

function Counter() {
  const [count, setCount] = useState(0);  //利用解构赋值
  
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

2. 参数说明

  • 初始值参数useState(initialValue)接受一个参数作为状态的初始值
  • 返回数组:包含当前状态值和一个更新状态的函数

3. 最佳实战

我们在使用useState时的时候,首先,为状态变量和更新函数赋予语义化名称(如 [user, setUser]),避免模糊命名;其次,始终通过创建新值来更新状态(如使用展开运算符处理数组/对象),而非直接修改原状态,以确保 React 能正确触发渲染。此外,避免滥用 useState,非响应式数据(如派生值)可直接用变量或 useMemo 处理。



单项绑定

请注意,React 的核心设计思想之一就是单向数据流(Unidirectional Data Flow),这种模式也常被称为单向绑定。有的学者可能之前学过Vue,了解过双向绑定,但是请区分它们,请看下面的介绍!

单向绑定是指数据在应用中只有一个方向的流动

  1. 状态(State)  作为唯一数据源
  2. 状态变化 触发 UI 更新
  3. UI 交互 通过预定义的回调函数修改状态
const [value, setValue] = useState(''); // 状态声明

// 数据流动方向:
// value → 显示在输入框 (State → View)
// 用户输入 → 调用setValue → 更新value (View → State → 重新渲染)
<input 
  value={value} 
  onChange={(e) => setValue(e.target.value)} 
/>

对比双向绑定

与双向绑定对比,单向绑定体现在:

  1. 数据流动的单一方向
  2. 更新必须通过setState

双向绑定(如 Vue 2.x):

<!-- 数据自动双向同步 -->
<input v-model="message">

React 单向绑定:

// 需要显式处理两个方向
<input 
  value={message} 
  onChange={(e) => setMessage(e.target.value)} 
/>

虽然你初次学习,可能觉得React这种单向绑定比较麻烦,但其实它的性能是比双向绑定的Vue要好的,这也是为什么很多大厂都在用React的原因。




🔧综合实例

下面我们通过一个简单的实战案例,来巩固一下上面的学习。

在本案例中,我们完全使用React实现基础的表单功能:

动画.gif

1.清空根组件

之前我们提到过,App.jsx是根组件,所以我们打开项目映入眼帘的就是App.jsx跟组件渲染的页面。 但我们现在是要做一个新项目,所以我们将App.jsx的内容一键清空,待会再将我们做好了的组件给放进去即可。

2.创建components

为了方便管理,我们在src下创建一个components文件夹,存放所有的组件文件,同时在src下创建一个css文件夹,存放所有组件会用到的样式。

3.TodoList组件

首先我们创建第一个组件文件,名为TodoList.jsx,这个组件就是我们的主页面,待会我们要将这个页面放到App.jsx中,就可以在打开项目时看到我们的TodoList.jsx渲染的页面了。

我们先为这个页面写一个Title,使用我们上面讲解过的useState进行声明。

const [title,setTitle] = useState("Todo List")

这样,我们就利用解构赋值得到了当前状态值title更新状态的函数setTitle,并且我们指定了当前状态值初始值为"Todo List"



我们测试一下这个setTitle的效果吧,书写以下代码:

    setTimeout(() => {
        setTitle("变化后的Titile") //3秒后更新title状态值为"变化后的Title"
    }, 2000)

动画.gif

可以看到setTitle能有效地帮我们动态改变了页面上的title,并且我们可以用浏览器的检查,发现title的值也发生了改变

image.png image.png

我们继续在TodoList中书写表格的初始值,待会要通过Todos组件显示表格,用TodoForm组件创建表单来修改表格。

const [todos,setTodos] = useState([
        {   
            id: 1,
            text: "吃饭",
            completed: false
        }
    ])


4.Todos组件

写完TodoList的基本结构之后,我们就来书写Todos组件,用来显示表格具体的表格,在这个组件中,我们使用Todos函数得到html页面,return(...)中使用{}来拿到我们的值,我们是通过数组的map方法来对todos进行处理,得到处理过后的数组,放到{}里面。

import '../css/Todo.css'
// 列表的渲染
function Todos(props){
    //如何拿到父组件传过来的数据呢?? 传参
    const todos = props.todos
    return (
        <ul>
            {
                todos.map(todo =>
                <li key = {todo.id}>{todo.text}</li>
                )
            }
        </ul>   
    )
}

export default Todos;

image.png

我们同时会写Todos.css,将其放在css文件夹下,在Todos组件中通过import '../css/Todo.css'引入css就可以看到直接生效了。

最后,因为我们是要将这个Todos放到我们的主页面TodoList,所以需要使用模块化导出,这里由于就一个组件,我们使用默认导出即可。

5.TodoForm组件

import { useState } from 'react'
import '../css/TodoForm.css'
function TodoForm(props) {
    const onAdd = props.onAdd
    const [text, setText] = useState('')

    const handleSubmit = (e) => {
        // 阻止默认行为
        // 由js 来控制
        e.preventDefault(); // event api 
        // console.log(text);
        onAdd(text)
        // todos?  打报告
    }

    const handleChange = (e) => {
        setText(e.target.value)
    }
    return (
        <form action="http://www.baidu.com" onSubmit={handleSubmit}>
            <input 
                type="text" 
                placeholder="请输入待办事项"
                value={text}
                onChange={handleChange}
            />
            <button type="submit">添加</button>
        </form>
    )
}

export default TodoForm;

我们首先,使用jsx语法在函数内创建表单,对表单内input添加一个onChange={handleChange}回调函数,当我们更改input里面的内容text时,回调函数handleChange,会使用更改状态值的setText函数进行text值的更改,此时才是真正的更改了text的值,也就是单向绑定。

接着,在提交表单时,触发了handleSubmit回调函数,它首先调用 e.preventDefault() 阻止默认的表单提交行为(防止页面刷新) 然后调用父组件通过 props 传递下来的 onAdd 函数,将当前 text 值传递给父组件,这个设计遵循了React的数据流原则:子组件通过调用父组件传递的函数来与父组件通信

你可能会说为什么要那么麻烦,还要用useState来管理input里面的内容text,也许在Vue中,直接使用双向绑定的特点就能实现上面的效果了,这就是很多人放弃学React的地方,但是我要告诉你的是, React 这样设计是有原因的,它提供了更明确的数据流控制更强的可预测性,这也是它作为大型框架的原因

6.TodoList处理表单

TodoList通过传递props给子组件:<TodoForm onAdd={handleAdd}/>,然后接收到TodoForm子组件传入的参数text,实现更新父组件的handleAdd,将数据表现在Todos上,子组件Todos也就可以实时更新父组件的props,此时就可以完成最终的效果了!


    const handleAdd = (text) => {
        setTodos([
            ...todos,
            {
                id: todos.length + 1,
                text: text, // 新的待办事项文本
                completed: false // 新的待办事项是否完成
            }
        ]

        )
    }


接着给出TodoList的源代码:

【点此展开】TodoList源代码:
// 内置的hook 函数
import {useState} from 'react' //引入react中的useState钩子函数,用于管理组件状态,useState返回一个数组,第一个元素是当前状态值,第二个元素是一个函数,用于更新状态值。

import TodoForm from './TodoForm' //引入TodoForm组件
import Todos from './Todos' //引入Todos组件
import '../css/TodoList.css'
function TodoList(){
        const [todos,setTodos] = useState([
            {   
                id: 1,
                text: "吃饭",
                completed: false
            }
        ])
    const [title,setTitle] = useState("Todo List") //初始化title状态值为"Todo List",setTitle函数用于更新title状态值

    const handleAdd = (text) => {
        setTodos([
            ...todos,
            {
                id: todos.length + 1,
                text: text, // 新的待办事项文本
                completed: false // 新的待办事项是否完成
            }
        ]

        )
    }
    setTimeout(() => {
        setTitle("变化后的Titile") 
    }, 2000)
    return (
        <div className="container"> 
            <h1 className='title'>{ title }</h1>
            {/* 表单 */}
            <TodoForm onAdd={handleAdd}/>
            {/* 列表 */}
            <Todos todos = {todos}/></div>
    )
}

export default TodoList; //向外输出该组件


📝总结

本文系统性地介绍了React的基础知识和核心概念,从项目初始化到组件化开发,再到响应式数据的处理,通过一个完整的TodoList案例帮助读者快速上手React开发。文章详细讲解了JSX语法、模块化导入导出、useState的使用以及单向数据流等React核心特性,并提供了清晰的目录结构和代码示例,为React初学者提供了全面的学习路径。通过阅读本文,读者不仅能够理解React的基本原理,还能掌握实际开发中的最佳实践,为进一步深入学习React打下坚实基础。

🌇结尾

本文部分内容参考XXX的:react中文文档

感谢你看到最后,最后再说两点~
①如果你持有不同的看法,欢迎你在文章下方进行留言、评论。
②如果对你有帮助,或者你认可的话,欢迎给个小点赞,支持一下~
我是3Katrina,一个热爱编程的大三学生

(文章内容仅供学习参考,如有侵权,非常抱歉,请立即联系作者删除。)

作者:3Katrina
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。