react--学习笔记
学习资源
- 官方文档:https://react.dev/
- React 教程中文版:https://zh-hans.react.dev/learn
- React Router:https://reactrouter.com/en/main
- Redux Toolkit:https://redux-toolkit.js.org/
- TypeScript + React:https://www.typescriptlang.org/docs/handbook/react.html
- 框架语法特性对比-中文版:https://component-party.lainbo.com/
- 大模型老师
JSX语法
JSX是React的模板语法,属于语法糖,最终会被 Babel编译为React.createElement()
jsx是将html和javascript结合起来的模板语法,但它不是 HTML,而是 JavaScript 的扩展
比如说
<div className="title">Hello</div>
会被编译成React.createElement("div", { className: "title" }, "Hello")
基本语法规则
- html根在return()的返回中
- JSX只能返回一个根元素
- 所有标签必须闭合
1
2
3
4return(
// 里面的标签必须是闭合的
// 只能有一个父div,如果要有两个父div,那么使用空标签<></>把他俩进行包裹
)
插值语法与数据渲染
- JSX的插值语法:标签内容,标签属性
数据渲染—条件渲染
- 三元运算符
{isLoading ? <Spinner /> : <Content />
- 逻辑与运算符(适用于存在则显示)
{error && <p className="error">{error}</p>}
- if判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function App(){
const divContent = '标签内容'
const divTitle = '标签标题'
const flag = true
let newContent=''
if(flag){
newContent = <span>JSP不用字符串包裹的写</span>
}else{
newContent = <p>这样就可以呈现啦</p>
}
return (
<div title = {divTitle}>{divContent}
{/* 注意这里的属性值{divTitle}不能加双引号哦 */}
{newContent}
</div>
)
}
export default App;
数据渲染—列表渲染(map+key)
使用Array.map()生成元素列表,必须添加 key 属性:
这里的Fragment其实等价于<></>,不渲染真实的dom结构
1 | import {Fragment} from "react" |
Fragment
Fragment(相当于template)
轻量级容器,当需要返回多个同级元素而不想引入额外DOM节点时,可以使用<></>或 <Fragment/>
,不渲染真实的dom结构
事件绑定
事件的基本用法
事件名使用驼峰命名(onClick、onChange)
传递函数引用,而非函数调用
传参的话可以使用箭头函数,例如()=>handleClick(1)
1 | // 正确 |
1 | function App(){ |
useState 状态处理
理解函数式组件
React 应用的基本构建块,本质是返回 JSX 的函数
vue可以声明响应式数据,可以驱动页面内容进行更新,也是减少操作DOM的手段,但是在react默认没有这个机制,所以这个数据也就是一个普通变量,把它改了页面内容不会更新
函数式组件默认是没有状态的,理解:
比如说点击按钮改变文本内容,如果按照下面这样写是不能改变的
1 | function App(){ |
useState基本使用
在react中使用useState函数进行状态变更
const [本次渲染的内容, 修改内容的函数] = useState(‘初始值’)
为什么react里这样做
如果每个变量都默认有状态更新机制的话,那页面的负担会很大
每个组件中通常只会有两三个变量是状态,不会每个变量都是状态
仔细看下面代码
1 | import {useState} from "react" |
状态的类型—字符串类型(如上面的代码)
状态类型—对象类型(关联数据通常使用)
比如对title和contet进行处理
1 | function App(){ |
状态类型—数组
比如添加 ,删除,排序,修改等
1 |
|
状态更新注意事项
useState结构的第二项(修改内容的函数)是进行覆盖操作
什么意思呢,比如说使用对象类型,原本有title和content两个属性,但我现在只想修改title属性,我想这样写行不行
1 | setData( |
这样不行的,覆盖后原来的content属性和属性值会丢失
正确写法是先通过展开运算符进行展开操作,把原来的属性都放进来,再修改同名属性
1 | setData( |
常见误区
状态更新是异步的,复杂场景使用函数式更新
对象 / 数组状态更新时,必须创建新对象 / 数组(不可变数据原则)
避免直接修改状态,始终通过 setter 函数更新
1 | // 错误:直接修改状态 |
React DOM组件的Props设置
基本属性映射规则
React中的DOM属性与HTML有写差异,需注意以下几点:
- class变为className
<div className="container">内容</div>
- 短横线属性用驼峰命名法
<div style={{ backgroundColor: 'red', fontSize: 16 }}></div>
- style 属性的两种写法
1
2
3
4
5
6
7
8
9
10
11// 对象内联写法
<div style={{ color: 'blue', margin: '10px' }}>文本</div>
// 提取样式对象
const styles = {
color: 'blue',
margin: '10px',
borderRadius: '4px'
};
<div style={styles}>文本</div> - 带有alt属性的img标签,alt要留在img标签内不能提取走
1
2
3const imgProps = { src: 'image.jpg', width: 200 };
// 显式添加 alt
<img {...imgProps} alt="示例图片" />
自定义组件的 Props(父传子)
JSX的展开语法不是ES6的展开运算符
ES6的展开运算发无法单独使用,不能在没有容器的地方单独使用
常用于展开,合并和修改操作
- JSX 的展开语法(常用于组件传参)
1
2
3
4
5
6
7function MyComponent({ name, age }) {
return <p>{name} is {age} years old.</p>
}
const user = { name: 'Alice', age: 25 }
<MyComponent {...user} />
//以上的写法等价于
<MyComponent name="Alice" age={25} />
组件复用:复用逻辑与样式,不复用内容
1 | // 可复用的按钮组件(只定义样式和逻辑) |
children属性:用于传递组件内部的子元素
1 | // 卡片组件 |
深拷贝与浅拷贝(React实例)
浅拷贝:
创建一个新对象或数组,但仅复制一层数据,对于引用类型(对象 / 数组),只复制其内存地址,新旧数据共享同一引用。
特点:修改新数据的引用类型属性时,原数据会同步变化
基本数据类型(如 number, string, boolean 等)会被复制一份新的值
引用类型(如 object, array)仅复制其引用地址,而不是深层的对象或数组本身
常见实现方式:展开运算符(…);Object.assign();Array.prototype.slice()/concat()
1 | const original = { a: 1, b: { c: 2 } }; |
深拷贝
定义:创建一个新对象或数组,递归复制所有层级的数据,新旧数据完全独立,互不影响。
特点:所有层次的数据都被复制,包括基本数据类型和引用类型
新对象或数组不会受到对原始对象或数组修改的影响
常见实现方式:JSON.parse(JSON.stringify())(简易版);递归函数手动复制;第三方库(如lodash.cloneDeep)
1 | const original = { a: 1, b: { c: 2 } }; |
react代码示例
浅拷贝示例
1 | // 场景:React中状态为对象时的浅拷贝问题 |
深拷贝实例
1 |
|
小结
在 React 应用中,当涉及到状态管理时,通常需要注意浅拷贝的行为,特别是在更新包含嵌套对象或数组的状态时,可能需要手动实现深拷贝或者使用 Redux Toolkit 这样的工具提供的便捷方法来帮助管理状态
使用场景
- 浅拷贝适用场景:
数据层级单一(如只有一层对象 / 数组)
引用类型属性不需要完全隔离(如仅修改基本类型属性)
React 中常用的…prevState展开运算符即浅拷贝 - 深拷贝适用场景:
需要处理深层结构的数据并且希望这些数据之间互不影响
数据多层嵌套(如obj.a.b.c)
需要完全隔离新旧数据(如撤销 / 重做功能、多版本数据对比)
性能注意事项
深拷贝比浅拷贝性能消耗更高,尤其是大数据量时
优先使用浅拷贝,仅在必要时使用深拷贝(如嵌套数据修改)
ES6展开运算符与JSX展开语法
ES6 展开运算符 和 JSX 展开语法 都是基于浅拷贝机制,它们只复制对象或数组的第一层数据
ES6 展开运算符主要用于创建对象或数组的浅拷贝。这意味着如果你有一个包含嵌套对象或数组的对象,使用展开运算符只会复制最外层的对象或数组,而内部的引用类型仍保持原有的引用关系
JSX 的展开语法 {…props} 在传递 props 到组件时也是基于浅拷贝原理。它只是将对象的所有键值对作为单独的 prop 传递给组件,而不涉及对象内部引用类型的深层次复制