React基础知识点
创建项目
npx create-react-app 你的项目名称- 创建好之后启动
npm run start- 删除多余的文件 安装normalize.css
npm i normalize.css目录结构
下面是一个推荐使用的 React 项目目录结构,基于 create-react-app 创建后进行调整,适用于中大型业务项目:
my-react-app/
├── public/ # 静态资源(不会被 webpack 处理)
│ ├── favicon.ico # 网站图标
│ ├── index.html # HTML 模板
│ ├── logo192.png # PWA 小图标
│ ├── logo512.png # PWA 大图标
│ ├── manifest.json # PWA 配置
│ └── robots.txt # 爬虫协议
├── src/ # 源代码目录
│ ├── api/ # 接口请求层(与后端交互)
│ │ ├── index.js # axios 实例封装(请求/响应拦截器)
│ │ ├── user.js # 用户相关接口
│ │ └── product.js # 商品相关接口
│ ├── assets/ # 静态资源(会被 webpack 处理)
│ │ ├── images/ # 图片资源
│ │ ├── styles/ # 全局样式
│ │ │ ├── index.css # 全局样式入口
│ │ │ ├── normalize.css # 样式重置(已安装)
│ │ │ └── variables.scss # SCSS 变量
│ │ └── fonts/ # 字体文件
│ ├── components/ # 公共组件(无业务逻辑、可复用)
│ │ ├── Button/ # 单个组件独立目录
│ │ │ ├── index.jsx # 组件实现
│ │ │ └── index.module.scss # 组件样式
│ │ ├── Modal/
│ │ │ ├── index.jsx
│ │ │ └── index.module.scss
│ │ └── NavBar/
│ │ ├── index.jsx
│ │ └── index.module.scss
│ ├── pages/ # 页面级组件(与路由一一对应)
│ │ ├── Home/ # 首页
│ │ │ ├── index.jsx # 页面组件
│ │ │ └── index.module.scss # 页面样式
│ │ ├── Login/ # 登录页
│ │ │ ├── index.jsx
│ │ │ └── index.module.scss
│ │ └── Detail/ # 详情页
│ │ ├── index.jsx
│ │ └── index.module.scss
│ ├── router/ # 路由配置
│ │ └── index.jsx # 路由表(使用 react-router-dom v6)
│ ├── store/ # 全局状态管理(Redux / Zustand / Mobx)
│ │ ├── index.js # store 入口
│ │ ├── slices/ # Redux Toolkit 的 slice 模块
│ │ │ ├── userSlice.js
│ │ │ └── cartSlice.js
│ │ └── actions.js # action 定义
│ ├── hooks/ # 自定义 Hook(封装可复用逻辑)
│ │ ├── useRequest.js # 通用数据请求 hook
│ │ └── useAuth.js # 登录态相关 hook
│ ├── utils/ # 工具函数(纯函数、无副作用)
│ │ ├── format.js # 格式化工具(日期、价格等)
│ │ ├── storage.js # 本地存储封装(localStorage)
│ │ └── request.js # 请求工具封装
│ ├── constants/ # 常量定义
│ │ └── index.js # 全局常量
│ ├── layouts/ # 布局组件
│ │ ├── DefaultLayout.jsx # 默认布局(含头部、底部)
│ │ └── BlankLayout.jsx # 空白布局(登录页等)
│ ├── config/ # 全局配置
│ │ └── index.js # 环境变量、接口地址等
│ ├── App.jsx # 应用根组件
│ ├── App.module.scss # 根组件样式
│ ├── index.js # 项目入口文件(ReactDOM.render / createRoot)
│ └── reportWebVitals.js # 性能监控(create-react-app 自带)
├── .eslintrc.js # ESLint 配置
├── .prettierrc # Prettier 配置
├── .gitignore # Git 忽略文件
├── package.json # 项目依赖与脚本
├── README.md # 项目说明
└── yarn.lock / package-lock.json # 包锁文件目录说明
public/:放置不需要编译的静态资源,例如 HTML 模板、图标、manifest.json等。文件夹内的文件会原样拷贝到构建产物根目录。assets/:放置会被 webpack 处理(哈希、压缩等)的静态资源,例如通过import引入的图片、SCSS 等。components/:通用组件目录,建议每个组件单独建文件夹,便于按需引入。pages/:页面级组件,与路由表中的路由一一对应,承载业务页面逻辑。router/:集中管理路由配置,配合react-router-dom v6使用。store/:全局状态管理目录,推荐使用 Redux Toolkit 或 Zustand。hooks/:自定义 React Hook,封装通用逻辑(如请求封装、权限校验等)。utils/:纯工具函数,与业务解耦,便于测试和复用。layouts/:通用布局组件,避免在每个页面中重复编写头部、底部等结构。config/:集中管理环境变量、接口域名、第三方 key 等配置项。
简化版(小型项目)
如果是 demo 或小项目,可以使用更轻量的目录结构:
my-react-app/
├── public/
│ ├── index.html
│ └── favicon.ico
└── src/
├── assets/
│ ├── images/
│ ├── styles/
│ │ ├── index.css
│ │ └── normalize.css
│ └── fonts/
├── components/ # 公共组件
├── pages/ # 页面组件
├── App.jsx
├── App.css
├── index.js
└── index.css命名规范
- 文件夹名称使用小写或小驼峰,如
components、myComponents。 - 组件文件使用大驼峰(PascalCase) 命名,如
HomePage.jsx、MyButton.jsx。 - 非组件文件(工具、配置)使用小驼峰命名,如
formatDate.js、request.js。 - 样式文件建议使用 CSS Modules(
xxx.module.scss)避免样式全局污染。
index.js (React项目入口文件)
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "normalize.css";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);JSX
即 js 和 html 语法写在一起
基本语法
函数组件
return后面的小括号(),不换行可以不用写小括号jsx 只能返回一个根元素,如果必须设计多元素,两个方法:
- 用Fragment包裹多个元素
- 用空标签包裹住(渲染的时候不产生多余的标签)
return (
<>
<div>1</div>
<div>2</div>
</>
);数据插值
基础使用
变量必须用大括号包裹,不能用引号包裹
变量可以是js表达式,也可以是函数
插值可以使用的位置: 1.标签位置 2.标签内容
function App() {
const divContent = '标签内容'
const divTitle = '标签标题'
return (
<div title={divTitle}>{divContent}</div>
)
}但是你要是用引号包裹了变量,就会变成字符串,而不是变量
<div title="{divTitle}">{divContent}</div>条件渲染
function App() {
const divTitle = "标签标题";
let divContent = null;
const flag = false;
if (flag) {
divContent = (
<div>
<span>flag为true</span>
</div>
);
} else {
divContent = (
<div>
<p>flag为false</p>
</div>
);
}
return <div title={divTitle}>{divContent}</div>;
}注意这里不要加引号写成
divContent = '<span>flag为true</span>'return里面除了三元表达式以外.其余都不行.比如if else,for,while,do while之类的循环语句
列表渲染
import { Fragment } from 'react'
function App() {
const list = [
{id: 1,name:'小吴'},
{id: 2,name:'小李'},
{id: 3,name:'小花'},
]
const listContent = list.map(item=>(
<Fragment key={item.id}>
<li>{item.name}</li>
<li>---------</li>
</Fragment>
))
return (
<ul>{listContent}</ul>
)
}由于只能有一个根目录,所以map下两个li元素可以写成如下形式
const listContent = list.map(item=>(
<>
<li key={item.id}>{item.name}</li>
<li>---------</li>
</>
))如果<li>---------</li>也需要遍历展示呢?
我们知道 空标签<></>是不会被渲染成结构的,因此key定义在空标签上是不会生效的
这时可使用 React提供的Fragment标签,同时要记得在前面import引用该标签
事件处理
- jsx 属性大多使用驼峰命名
function App() {
function handleClick(e){
console.log('点击按钮',e)
}
return (
<button onClick={handleClick}>按钮</button>
)
}React组件基础使用
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI,渲染组件只需要把组件当成标签书写即可
useState 基本使用
useState是一个React Hook,它允许我们向组件添加一个状态变量。
本质:和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会跟着变化(数据驱动视图)
const [count, setCount] = useState(0);useState 是一个函数,返回值是一个数组
数组中的第一个参数是状态变量,第二个参数是set函数用来修改状态变量
useState的参数可以作为count的初始值
字符串,数字的更改
import { useState } from "react";
// 项目的根组件
function App() {
let [count, setCount] = useState(0)
const handleMyButton = () => {
setCount(()=>{
return count + 1
})
}
return (
<div className="App">
<button onClick={handleMyButton}>点击增加{count}</button>
</div>
);
}
export default App;- 对象状态的更改
import { useState } from 'react'
function App() {
const [data,setData] = useState({
title:'默认标题',
content:'默认内容'
})
function handleClick(){
setData({
...data,
title: '新标题'
})
}
return (
<>
<div title={data.title}>{data.content}</div>
<button onClick={handleClick}>按钮</button>
</>
)
}对于对象的状态操作,其实和字符串差不多,唯一一个要注意的点是,在setData修改内容的时候,不能只写修改内容的操作,即
function handleClick() {
setData({
title: "新标题",
});
}这样会造成其他属性内容的丢失,在修改属性操作前加上...data拓展操作,即把其他不变属性也添加进来,便不会造成内容丢失
- 数组的状态更改
function App() {
const [data,setData] = useState([
{id: 1,name:'小吴'},
{id: 2,name:'小李'},
{id: 3,name:'小花'},
])
const listData = data.map(item=>(
<li key={item.id}>{item.name}</li>
))
function handleClick(){
setData(data.filter(item=>item.id!==2))
}
return (
<>
<ul>{listData}</ul>
<button onClick={handleClick}>按钮</button>
</>
)
}className 类名
在React中 className 就是类名,而不是class属性
import { useState } from "react";
import "./App.css";
import classNames from 'classnames';
// 项目的根组件
function App() {
// 使用 const 定义,useState 返回值不需要重新赋值
const [count, setCount] = useState(0);
// 行内样式对象(不推荐大量使用,权重高不易覆盖)
const style = {
color: 'blue'
};
// 函数式更新:基于最新值计算,避免连续调用时丢更新
const handleMyButton = () => {
setCount(() => count + 1);
};
return (
<div className="App">
{/* 行内样式,不推荐 */}
<span style={style}>测试一下</span>
{/* 通过类名设置样式,className 写法等同于原生 class */}
<button
onClick={handleMyButton}
// 静态类名直接写字符串
className="my-btn"
// 多类名拼接:普通写法 "my-btn is-active"
// className="my-btn is-active"
>
点击增加{count}
</button>
</div>
);
}
export default App;组件通信和插槽
一些基础语法
- 看起来像html 属性的功能,在react中称为
为DOM组件设置Props
举例
import image from "XXX";
<img src={image} alt="" />;对于src第一种方法是 import(如示例),第二种方法是使用在线地址
举例2 vue 中的class属性 对应react中className属性
<div className="App">
<h1>Hello React</h1>
</div>举例3
- style 属性采用键值对写法
style={{
width:100, // width: '100px'
height:100
}}如果要加单位,要添加引号
但是更推荐下面的写法
const imgStyle = {
xxx样式内容,
};
style = { imgStyle };举例4
在react使用css中带-的属性,例如background-color,一般要写成驼峰形式,如backgroundColor
jsx展开语法和props
import image from "./logo.svg";
function App() {
const imgData = {
className: "small",
style: {
width: 100,
height: 100,
backgroundColor: "grey",
},
src: image,
};
return (
<div>
<img alt="" {...imgData} />
</div>
);
}注意
在一个标签里设置太多属性,书写繁琐
react便提供了一种展开语法,将属性集中写在一个对象中,再应用在标签内(其中{...imgData}中的大括号是jsx单独的功能支持)
注意: jsx的展开操作不是es6中的展开运算符
jsx的展开操作必须写在一个容器中,例如
<img/>,但是es6的展开运算符是可以单独暴露出来的,如const a = {...b}但是如果是要打印
console.log()imgData中的内容,不能写成console.log(...imgData),要写成console.log({...imgData})
react自定义组件
function Article(props) {
return (
<div>
<h2>{props.title}</h2>
<p>{props.content}</p>
<p>状态{active ? "显示中" : "已隐藏"}</p>
</div>
);
}
export default function App() {
return (
<>
<Article title="标题1" content="内容1" />
<Article title="标题2" content="内容2" />
<Article title="标题3" content="内容3" />
</>
);
}也可以不写成props点xxx的形式,需要使用到es6的解构
function Article({ title, content, active }) {
return (
<div>
<h2>{title}</h2>
<p>{content}</p>
<p>状态{active ? "显示中" : "已隐藏"}</p>
</div>
);
}react 组件使用props
父传子,传递的值不可修改
function Detail({ content, active }) {
return (
<>
<p>{content}</p>
<p>状态{active ? "显示中" : "已隐藏"}</p>
</>
);
}
function Article({ title, articleData }) {
return (
<div>
<h2>{title}</h2>
<Detail {...articleData} />
</div>
);
}
export default function App() {
const articleData = {
title: "标题1",
detailData: {
content: "内容1",
active: true,
},
};
return (
<>
<Article {...articleData} />
</>
);
}React 插槽功能
- 默认卡槽通过children传递
function List({ children }) {
return <ul>{children}</ul>;
}
export default function App() {
return (
<>
<List>
<li>列表项1</li>
<li>列表项1</li>
<li>列表项1</li>
</List>
<List>
<li>列表项2</li>
<li>列表项2</li>
<li>列表项2</li>
</List>
</>
);
}- 具名卡槽
function List({ children, title, footer = <div>默认底部</div> }) {
return (
<>
<h2>{title}</h2>
<ul>{children}</ul>
{footer}
</>
);
}
export default function App() {
return (
<>
<List title="标题1" footer={<p>这是底部内容1</p>}>
<li>列表项1</li>
<li>列表项1</li>
<li>列表项1</li>
</List>
<List title="标题2" footer={<p>这是底部内容1</p>}>
<li>列表项A</li>
<li>列表项B</li>
<li>列表项C</li>
</List>
<List title="标题3">
<li>列表项X</li>
<li>列表项Y</li>
<li>列表项Z</li>
</List>
</>
);
}子组件通过父组件方法修改状态
import { useState } from "react";
function Detail({ onActive }) {
const [status, setStatus] = useState(false);
function handleClick() {
setStatus(!status);
onActive(status);
}
return (
<div>
<button onClick={handleClick}>按钮</button>
<p
style={{
display: status ? "block" : "none",
}}
>
Detail的内容
</p>
</div>
);
}
export default function App() {
function handleActive(status) {
console.log(status);
}
return (
<>
<Detail onActive={handleActive} />
</>
);
}