React学习笔记
1.React是什么
用于构建用户界面(UI)的开源 Js库, 将数据渲染成html视图; 最初由 Facebook(现 Meta)开发并维护,旨在提供一种高效、灵活且可重用的 UI 组件开发方式。
核心工作原理与特性
组件化架(Component-based):React 将复杂的页面拆分为独立、可复用的“组件”(如按钮、导航栏、整个页面)。每个组件都包含自己的逻辑和外观,这使得代码更加模块化、易于维护。
虚拟 DOM(Virtual DOM):这是 React 高效的关键。当组件的状态发生变化时,React 会先在内存中更新轻量级的“虚拟 DOM”,然后与真实的 DOM 进行对比(Diff 算法),最后只将发生变化的部分更新到真实页面上,从而极大地减少了浏览器的重绘开销,提升了性能。
声明式设计与单向数据流:React 采用声明式范式,开发者只需描述界面“应该长什么样”,当数据变化时界面会自动更新。同时,它遵循单向数据流(通过
props从父组件向子组件传递数据),保证了数据的可控性和可预测性。关键语法与开发模式
JSX 语法:开发者在 js中直接编写类似 HTML 的结构。这打破了传统前端将结构与逻辑分离的做法,让渲染逻辑和内容展示维护在同一个地方,开发更加直观。
React Hooks:Hooks(如
useState、useEffect)是 React 的重大创新,它允许开发者在不编写类(Class)的情况下,在函数组件中使用状态管理和其他 React 特性,大幅简化了代码结构。服务端组件(Server Components):较新的特性,允许组件仅在服务器端运行,有助于实现前后端工作分离,进一步提升应用性能和数据获取能力。
生态系统与跨平台能力
丰富的生态:React 拥有极其庞大的社区和第三方库支持,开发者可以方便地集成路由、状态管理等工具,灵活选择适合自己项目的技术栈。
跨平台开发:除了用于构建 Web 单页应用(SPA),React 的设计思想还衍生出了 React Native,允许开发者使用类似 React 的语法来开发 iOS 和 Android 原生移动应用,实现“一次学习,多端开发”。
React对比Vue
1. 核心语法与设计哲学
React:定位为“UI 库”,推崇极致的灵活性。它采用 JSX 语法,将 HTML 结构与 JavaScript 逻辑写在同一个文件中,要求开发者具备较强的函数式编程思维。React 采用单向数据流和不可变数据设计,状态变更需要手动调用
setState或useState的 setter 函数来显式触发重渲染。Vue:定位为“渐进式框架”,追求“自动高效”。它采用 Template(模板)语法,将 HTML、JS 和 CSS 分离在单文件组件中,对传统前端开发者非常友好。Vue 拥有强大的响应式系统(基于 Proxy),能够自动追踪数据依赖,数据变化时视图会自动更新,且支持双向数据绑定(
v-model)。2. 底层原理与渲染性能
React:采用 虚拟 DOM + Fiber 架构。当状态更新时,React 会重新执行组件函数生成新的虚拟 DOM 树,通过 Diff 算法计算最小变更。其 Fiber 架构支持并发渲染,可以将渲染任务拆分为小单元并支持中断,在处理复杂交互和高频更新时能提供极其流畅的用户体验。
Vue:同样采用虚拟 DOM,但结合了编译时优化。Vue 的模板编译器在构建阶段会进行静态分析(如静态提升、PatchFlag 靶向更新),跳过不必要的对比。此外,Vue 3 引入的 Vapor Mode 更是能将模板直接编译为极高效的命令式 DOM 操作指令,实现元素级精准更新,在中小型应用中性能表现极为出色。
3. 学习曲线与开发体验
Vue:学习曲线平缓,上手极快。官方提供了“全家桶”(Vue Router、Pinia 等),开箱即用,开发者无需在技术选型上花费大量时间,适合快速原型开发和中小型项目。
React:学习曲线较陡。由于 React 本身只是一个视图库,开发者需要自行在庞大的社区中挑选路由、状态管理(如 Redux、Zustand)等第三方库,并深入理解 Hooks 规则、闭包等 JavaScript 底层概念。适合大型复杂应用、高交互性产品
2.创建项目
使用vite创建:
npm create vite@latest my-react-app -- --template react
使用webpack创建:
npx create-react-app my-react-app
cd my-react-app
npm install
npm run dev
3.hooks
React Hooks是 React 16.8 引入的新特性,允许开发者在不编写类组件的情况下,在函数组件中使用state和其他 React 特性。简化了代码结构,提升了开发效率和组件的复用性。
不要在循环、条件语句(如 if)、嵌套函数或 try/catch 代码块中调用 Hook。必须确保每次渲染时 Hook 的调用顺序一致。
只能在 React 函数组件或自定义 Hook 的主体顶层调用,不能在常规的 JavaScript 函数或类组件中调用。
| useState | 在函数组件中添加和管理局部状态。它返回一个状态变量和一个更新该状态的函数 |
| useEffect | 在函数组件中执行副作用操作,如数据获取、订阅事件、手动更改 DOM 或设置定时器等。它接受一个函数和一个可选的依赖项数组作为参数 |
| useMemo | 用于缓存计算结果。当依赖项不变时,返回缓存的值,从而避免每次渲染都进行昂贵的重复计算 |
| useCallback | 用于缓存回调函数的引用。当依赖项没有变化时,它会返回缓存的函数,避免子组件不必要的重新渲染 |
| useRef | 用于在函数组件中创建并返回一个可变的 ref 对象,常用于访问 DOM 元素或在多次渲染之间保持可变值 |
| useContext | 在函数组件中访问和消费 React Context 的数据,避免组件逐层传递 props |
| useReducer | 类似Redux 的 reducer 函数,适合用于管理包含多个子值或复杂逻辑的本地状态 |
| useOptimistic | useOptimistic允许在进行异步操作(如网络请求)时乐观地更新用户界面,提升应用响应速度的体验。 |
3.1.useState
useState定义状态后,状态变化则函数组件会重新执行
初始值还可通过函数返回值来设置:const [state, setState] = useState(()=>{初始值}) 赋值前可以使用函数执行其他逻辑
setState中给的set函数是异步修改状态的,所以执行set函数后立马拿的state还是更新前的值
要拿状态更新后的最新值,要使用
useUffect(()=>{
console.log(state)
},依赖项变化时触发的回调函数,[state])
如果在set修改状态时,连续累计修改会有更新不及时的bug
setCount(count+1)
setCount(count+1)
实际并没加2,因为set函数是同步执行,但是set内部是异步的,第二次执行set函数时,第一次的set结果还没有执行完成,需要改成setcount(新值)
或setCount((prev)=>prev+1) // 如果新值基于旧值计算得到,需要通过函数形参拿到旧值,return 出新值
如果要更改对象类型的值,并触发组件的重新渲染,必须用展开运算符或Object.assign()来生成一个新对象,使用新对象覆盖旧对象才能触发组件的重新渲染(内部判断对象是否变化,只判断了引用地址)
user.name= 'new' // 不生效
// 用新对象的引用替换旧对象的引用才会触发重新渲染
setUser(...user)或setUser(Object.assign({},user))
使用useState来模拟强制刷新
const [, forceUpdate] = useState({})
const onRefresh = ()=>{forceUpdate({})}
useRef
const inputRef = useRef<HTMLInputElement>(null) // 元素的初始值都是null
<input type="text" ref={inputRef}
inputRef.current.focus()
使用useRef还可以存储组件渲染周期的共享数据,或者是set函数变更前的数据
组件重新渲染rerender时,useRef不会重复初始化
ref.current变化时,不会导致组件重新渲染
ref.current不可以当做其他hooks的依赖项
在父组件中无法使用ref来直接引用函数式子组件,因为默认情况下组件不会暴漏内部DOM节点的ref
const childRef = useRef(null)
<Child ref = {childRef} /> 会报错
需要使用React.forwardRef把函数式组件包装起来(他不是hooks,而是react提供的函数)
const Child = React.forward((props, ref)=>{
})
4.Redux和Mobx
MobX 和 Redux 是 React 生态中最主流的两个状态管理库。
Redux 遵循函数式编程,强调严谨和可预测性;而 MobX 采用响应式编程(面向对象),追求简洁和高效。
Redux 适用场景:
- 大型复杂项目:需要严格的单向数据流和强约束性,保证多人协作下的代码结构清晰。
- 需要时间旅行调试(Time-traveling):Redux 的不可变状态和纯函数机制天然支持状态回溯、撤销/重做(如画板应用、复杂表格应用)。
- 团队熟悉函数式编程:如果团队对纯函数、不可变数据有深入理解,能充分发挥 Redux 的优势。
MobX 适用场景:
- 中小型项目/快速迭代:上手简单,样板代码少,开发效率高。
- 团队熟悉面向对象编程(OOP):MobX 的 Class + 装饰器/响应式 API 写法更符合大多数开发者的直觉。
- 追求极致渲染性能:MobX 的自动依赖追踪机制能实现组件的精确更新,避免不必要的重渲染。
注:两者并非绝对互斥,也可以在项目中用 Redux 做全局状态管理,用 MobX 做组件局部的复杂状态管理器。
Redux
Redux 的核心概念包括:Store(单一数据源)、Action(描述动作的对象)、Reducer(处理状态变更的纯函数)。
核心特点:状态是不可变(Immutable)的,必须通过 dispatch(action) 触发更新,且 Reducer 必须是纯函数。
import { createSlice, configureStore } from '@reduxjs/toolkit';
// 1. 定义 Slice (包含 state 和 reducer)
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
// RTK 内部使用 Immer,允许写“可变”逻辑,但底层仍生成不可变状态
state.value += 1;
},
},
});
// 2. 配置 Store
export const store = configureStore({
reducer: { counter: counterSlice.reducer },
});
// 3. 在 React 组件中使用
// 读取状态: const value = useSelector(state => state.counter.value);
// 触发更新: useDispatch()(counterSlice.actions.increment());
MobX
MobX 的核心概念包括:Observable(可观察状态)、Action(修改状态的动作)、Computed(派生状态)、Observer(响应式组件)。
核心特点:状态是可变(Mutable)的,采用响应式编程自动追踪依赖,无需手动 dispatch。
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
// 1. 定义 Store (面向对象风格)
class CounterStore {
count = 0;
constructor() {
// 自动将属性标记为 observable,方法标记为 action,getter 标记为 computed
makeAutoObservable(this);
}
increment() {
this.count += 1; // 直接修改状态,自动触发视图更新
}
get doubleCount() {
return this.count * 2; // 自动缓存的派生状态
}
}
const counterStore = new CounterStore();
// 2. 在 React 组件中使用
const Counter = observer(() => {
return (
<div>
<p>{counterStore.count} - {counterStore.doubleCount}</p>
<button onClick={() => counterStore.increment()}>+1</button>
</div>
);
});
1. 核心设计哲学对比
-
Redux:基于推模型(Push)和函数式编程。状态只读,必须通过纯函数(Reducer)返回新状态。强调过程明确,每一步变更清晰可查。
-
MobX:基于拉模型(Pull)+ 响应式和面向对象编程。状态可变,通过 Proxy/getter/setter 自动感知依赖并更新。强调结果响应,开发体验流畅。
2. 状态更新机制的区别
-
Redux:通过
dispatch广播 Action,所有的 Reducer 都会被调用。更新粒度较粗,需要通过对比前后的 state(浅比较)来控制组件是否重渲染。 -
MobX:利用
observable实现精确更新。当状态变化时,MobX 自动追踪哪些组件依赖了该状态,只通知依赖的组件更新,无关组件不受影响。
3. 异步处理与副作用
-
Redux:原生 Reducer 必须是纯函数,不能包含异步操作。必须借助中间件(如 Redux-Thunk、Redux-Saga)来处理异步逻辑,学习曲线较陡。
-
MobX:没有中间件概念。在
action中可以直接使用async/await语法处理异步任务,代码更加简洁直观。
4. 调试与可预测性
-
Redux:可预测性极强。配合 Redux DevTools,可以轻松实现时间旅行调试、查看 Action 日志和状态快照,非常适合排查复杂 Bug。
-
MobX:由于状态修改路径较隐式(自动响应),调试相对困难,状态难以回溯。
5. MobX 底层原理
-
面试官可能会问 MobX 的响应式原理。核心在于:MobX 将数据包装为 Observable,当组件(Observer)渲染时,MobX 会通过
reaction.track拦截属性的 getter,自动收集依赖;当属性被修改(setter)时,自动通知收集到的依赖进行更新。
更多推荐
所有评论(0)