虚拟 DOM 的原理
虚拟 DOM(Virtual DOM,简称 VDOM)
简单来说,就是为了解决性能问题而设计的一套数据中转站和高效对比的机制。
主要包含三个核心步骤:
创建(Create):用数据构建出完整的 VDOM 树(JS 对象树)
对比(Diffing):找出新的 VDOM 树与旧的 VDOM 树之间差异
应用(Patching):将找出的差异应用到真实的 DOM 节点上。
VDOM Element 就是一个 JavaScript 对象,用来描述真实的 DOM 节点
React组件的生命周期
生命周期就是一个组件从创建到消亡的整个过程,它主要分为三个阶段:
挂载(Mounting / Initializing):组件首次被创建并插入到 DOM 中
更新(Updating):组件因为 props 或内部 state 的变化而重新渲染
卸载(Unmounting):组件从 DOM 中被移除并销毁
props 和 state 是 React 组件的“灵魂”
props(属性)
组件的配置清单,由父组件传递给组件
性质:外部给定的,不可变,子组件只能读取,不能修改
state(状态)
组件的内部记忆,由组件自身的内部管理
性质:内部维护的,可变,可以通过特定方法修改,如 setState 或 useState
例子
用于展示文章点赞数量的 <LikeButton> 组件
// 父组件 (ParentComponent) 负责设置标题
function ParentComponent() {
return (
// 父组件给子组件传递了一个 props
<LikeButton initialCount={10} buttonLabel="赞" />
);
}
// 子组件负责管理计数
function LikeButton(props) {
// 组件内部管理自己的状态
const [count, setCount] = React.useState(props.initialCount);
// ...
}类组件的经典生命周期方法
挂载
核心方法:componentDidMount()
作用:组件渲染(Render)完毕,并成功插入到 DOm 树后立即执行
实践:首次发送网络请求、设置定时器、添加事件监听器
更新
核心方法:componentDidUpdate(prevProps, prevState)
作用:组件因 props 或 state 变化重新渲染完毕后执行
实践:根据 prevProps 或 prevState 的变化来决定是否发送新的网络请求或执行其他操作
卸载
核心方法:componentWillUnmount()
作用:组件从 DOM 中移除和销毁前执行
实践:清除在 componentDidMount 中设置的定时器、事件监听、取消未完成的网络请求等
函数组件的生命周期管理
在函数组件中,我们不再使用上面的三个方法,而是使用一个功能强大的 Hook:useEffect
useEffect 的设计理念是让组件可以执行副作用(Side Effects),并且它总是在组件渲染完成后才执行
阶段
挂载
useEffect(() => {
/* 运行一次的逻辑 */
}, [ ])使用空的 依赖项数组 [],告诉 React:这个 Effect 只在组件首次渲染后运行一次(模拟 componentDidMount)
更新
useEffect(() => {
/* 依赖项变化时运行 */
}, [dep1, dep2])依赖项数组中包含 dep1 或 dep2 等 props 或 state。只要其中任何一个发生变化,Effect 就会重新运行(模拟 componentDidUpdate)
卸载
useEffect(() => {
return () => {
/* 清理逻辑 */
}
}, [ ])Effect 返回的函数就是清理函数。它会在组件卸载前运行(模拟 componentWillUnmount)
实践
function MyFunctionComponent() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
// 挂载后发送请求
fetch('api/data')
.then(res => res.json())
.then(data => setData(data));
// 清理函数:如果请求还没完成,可以在这里取消请求
return () => {
// cleanup logic (例如:清除定时器或取消订阅)
};
}, []); // 依赖数组为空,只在挂载时运行一次
return <div>{data ? data : '加载中...'}</div>;
}介绍 Refs 以及它的作用
什么是 Refs
Refs(References,引用)提供了一种允许访问在 render 方法中创建到 DOM 节点或 React 组件实例的方法
如果说 props 和 state 是组件的“数据流”,那么 Ref 就是一条绕开数据流的“秘密通道”,能够直接拿到一个真实的 DOM 元素或组件,并对其进行操作
主要作用
管理焦点、文本选择或媒体播放
例如:组件加载完成后,立即让某个输入框自动获取焦点
强制触发命令式动画
例如:在不触发组件重新渲染的情况下,直接修改 DOM 元素的样式属性
在函数组件中使用 Refs
function FocusInput() {
// 创建一个 Ref
const inputRef = React.useRef(null);
const handleClick = () => {
// 通过 .current 属性直接访问 DOM 节点,并调用原生方法
inputRef.current.focus();
};
return (
<>
<input type="text" ref={inputRef} /> {/* 将 Ref 挂载到元素上 */}
<button onClick={handleClick}>聚焦输入框</button>
</>
);
}介绍 key 以及它的作用
什么是 key
key 是在 React 中渲染列表时,为列表中的每个元素或组件所设置的一个特殊的、字符串类型的属性
作用
key 的存在是为了帮助 React 内部的 Diffing(对比)算法,让 React 可以最小化对真实 DOM 的操作,从而极大提高了渲染的性能和准确性
必须满足
唯一性:同一个列表中,所有兄弟元素之间
key必须是唯一的稳定性:元素
key在其整个生命周期中不应该发生变化
Redux 简介
Redux 是一个用于 JavaScript 应用到可预测状态容器,它被广泛用于管理大型 React 应用中的全局状态
为什么需要 Redux
在复杂的应用中,很多组件需要共享和修改同一份数据,如果不使用全局状态管理工具,就会出现以下问题:
Prop Drilling(逐层传递):为了将数据从顶层组件传给深层子组件,必须经过很多中间组件,导致代码难以维护
状态混乱:多个组件可以随意修改同一份数据,导致状态变化难以预测和调试
Redux 的作用就是为了整个应用提供一个集中的、单一的数据源,并强制执行严格的规则来管理状态的变化,从而让应用状态变得可预测、易于调试
三大核心组件部分
Redux 遵循单一数据源和单向数据流的原则,由以下三个核心部分驱动:
Store(存储):存储应用到所有状态。同一个 Redux 应用中只能有一个 Store
Action(动作):一个普通的 JavaScript 对象,用于描述发生了什么。它至少包含一
type字段Reducer(处理者):接收当前的 State 和触发的 Action,然后计算并返回一个新的 State,不修改原始 State
setState 的第二个参数是什么,作用又是什么?
setState 方法的第二个参数是一个可选的回调函数。它的作用是确保特定的代码逻辑在状态更新被应用并且组件重新渲染完成之后才执行
例子
假设有一个计数器,希望在计数器更新后,立即在控制台中打印最新的数值,并执行一个副作用(比如存储到本地):
错误示范
// 无法保证能拿到最新值
this.setState({
count: this.state.count + 1
});
// 此时 this.state.count 可能是旧值
console.log('新值:', this.state.count);正确做法
// 使用第二个回调参数
this.setState({
count: this.state.count + 1
}, () => {
// 保证此处的代码在 DOM 更新后执行
console.log('新值:', this.state.count);
// 可以在这里执行依赖新状态的副作用,比如存储到本地或操作 DOM
localStorage.setItem('count', this.state.count);
});替代方案
在现代函数组件中,已经不再使this.setState ,而是使useState useEffect 组合来达到同样的目的