✨前言
作者最近准备更新一个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模块化导入和导出的方式来进行组件的相互使用。
在实战中,我们的相关的一个或一些组件往往放在一个文件中,可以从一个文件中导入和导出多个组件。
而我们这些文件也会放在响应的开发目录中,不同目录文件夹下对应着不同功能的文件,每个文件里面包含着一类相关的组件,我们需要将文件,也就是组件,分好类,放在不同的文件夹下,这样的规范对我们的实战开发来说十分重要!下面就给大家介绍一些最佳实战的分类规范。
todoList是我们的项目目录,代表着整个项目,而src是我们的源代码目录,也是我们的开发目录,我们对一些组件的创建都是在src目录下的,因此我们着重讲解src目录。
其中:
- 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,了解过双向绑定,但是请区分它们,请看下面的介绍!
单向绑定是指数据在应用中只有一个方向的流动:
- 状态(State) 作为唯一数据源
- 状态变化 触发 UI 更新
- UI 交互 通过预定义的回调函数修改状态
const [value, setValue] = useState(''); // 状态声明
// 数据流动方向:
// value → 显示在输入框 (State → View)
// 用户输入 → 调用setValue → 更新value (View → State → 重新渲染)
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
对比双向绑定
与双向绑定对比,单向绑定体现在:
- 数据流动的单一方向
- 更新必须通过setState
双向绑定(如 Vue 2.x):
<!-- 数据自动双向同步 -->
<input v-model="message">
React 单向绑定:
// 需要显式处理两个方向
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
虽然你初次学习,可能觉得React这种单向绑定比较麻烦,但其实它的性能是比双向绑定的Vue要好的,这也是为什么很多大厂都在用React的原因。
🔧综合实例
下面我们通过一个简单的实战案例,来巩固一下上面的学习。
在本案例中,我们完全使用React实现基础的表单功能:
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)
可以看到setTitle
能有效地帮我们动态改变了页面上的title,并且我们可以用浏览器的检查,发现title的值也发生了改变
我们继续在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;
我们同时会写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
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。