React 核心原理与虚拟 DOM
中等 🟡React 生态
4 个标签
预计阅读时间:13 分钟
React虚拟 DOM组件状态管理
React 核心原理与虚拟 DOM
React 是一个用于构建用户界面的 JavaScript 库,其核心原理包括虚拟 DOM、组件化和单向数据流。
⚛️ 虚拟 DOM 原理
虚拟 DOM 是 React 性能优化的关键。它是一个轻量级的 JavaScript 对象,用来描述真实 DOM 的结构。当组件状态变化时,React 会先更新虚拟 DOM,然后通过 diff 算法计算出最小的 DOM 操作,最后批量应用到真实 DOM 上。
虚拟 DOM 的优势:
•减少直接操作 DOM 的次数,提高性能
•提供跨平台能力,如 React Native
•简化状态管理和组件更新逻辑
💻 代码示例:虚拟 DOM 对比
javascriptCode
// 真实 DOM 操作
const element = document.createElement('div');
element.className = 'container';
element.textContent = 'Hello, World!';
document.body.appendChild(element);
// 虚拟 DOM(React)
const element = React.createElement(
'div',
{ className: 'container' },
'Hello, World!'
);
// JSX 语法(编译为 React.createElement)
const element = (
<div className="container">
Hello, World!
</div>
);🔄 React 渲染流程示例
javascriptCode
// 1. 初始渲染
function App() {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
// 2. 状态变化
// 用户点击按钮,setCount(count + 1) 被调用
// 3. 创建新的虚拟 DOM
// React 根据新的状态创建新的虚拟 DOM 树
// 4. Diff 算法
// React 比较新旧虚拟 DOM 树,找出差异
// 5. 最小化 DOM 操作
// React 只更新变化的部分(例如,只更新文本节点)
// 6. 批量更新
// React 将多个状态更新合并为一次 DOM 操作🧩 组件化思想
React 的组件化思想让代码更加模块化和可维护:
•组件是独立的、可复用的代码单元
•每个组件有自己的状态和属性
•组件通过 props 传递数据,通过 state 管理内部状态
💻 代码示例:组件化实践
javascriptCode
// 函数组件
function Welcome(props) {
return <h1>Hello, {props.name}!</h1>;
}
// 使用组件
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
);
}
// 类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
// 组合组件
function Card({ title, content, footer }) {
return (
<div className="card">
<div className="card-header">{title}</div>
<div className="card-body">{content}</div>
<div className="card-footer">{footer}</div>
</div>
);
}
function App() {
return (
<Card
title="Card Title"
content={<p>Card content goes here.</p>}
footer={<button>Action</button>}
/>
);
}单向数据流
React 采用单向数据流,数据从父组件流向子组件:
•父组件通过 props 向子组件传递数据
•子组件通过回调函数与父组件通信
•状态管理更加清晰可预测
代码示例
单向数据流示例
javascriptCode
// 父组件
function Parent() {
const [count, setCount] = React.useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<h1>Parent Count: {count}</h1>
<Child count={count} onIncrement={increment} />
</div>
);
}
// 子组件
function Child({ count, onIncrement }) {
return (
<div>
<h2>Child Count: {count}</h2>
<button onClick={onIncrement}>Increment</button>
</div>
);
}
// 状态提升
// 多个子组件共享状态时,将状态提升到最近的共同父组件
function Parent() {
const [value, setValue] = React.useState('');
return (
<div>
<Input value={value} onChange={setValue} />
<Display value={value} />
</div>
);
}
function Input({ value, onChange }) {
return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}
function Display({ value }) {
return <p>Value: {value}</p>;
}生命周期
React 组件的生命周期包括挂载、更新和卸载三个阶段:
•挂载阶段:constructor → render → componentDidMount:React组件的生命周期挂载阶段包括三个主要方法,constructor是组件创建时调用的构造函数,用于初始化state和绑定方法,render方法负责返回JSX描述的UI结构,componentDidMount在组件挂载到DOM后立即调用,是进行DOM操作、网络请求、订阅事件等副作用操作的最佳时机,挂载阶段只执行一次,是组件生命周期的起点
•更新阶段:shouldComponentUpdate → render → componentDidUpdate:React组件的更新阶段在props或state变化时触发,shouldComponentUpdate用于决定是否需要重新渲染,返回false可以跳过渲染优化性能,render方法根据新的props和state返回新的JSX,componentDidUpdate在组件更新后调用,可以访问更新前的props和state,是进行DOM操作、网络请求等副作用操作的时机,更新阶段可能执行多次
•卸载阶段:componentWillUnmount:React组件的卸载阶段只包含componentWillUnmount一个方法,在组件从DOM中移除前调用,是进行清理操作的最佳时机,如清除定时器、取消网络请求、移除事件监听器、清理订阅等,componentWillUnmount只执行一次,是组件生命周期的终点,不正确的清理可能导致内存泄漏
代码示例
类组件生命周期示例
javascriptCode
class LifecycleComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log('Constructor');
}
componentDidMount() {
console.log('Component did mount');
// 发起网络请求
this.fetchData();
// 设置定时器
this.timer = setInterval(() => {
this.setState({ count: this.state.count + 1 });
}, 1000);
}
componentDidUpdate(prevProps, prevState) {
console.log('Component did update');
// 检查 props 或 state 是否变化
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
componentWillUnmount() {
console.log('Component will unmount');
// 清理定时器
clearInterval(this.timer);
// 取消网络请求
if (this.abortController) {
this.abortController.abort();
}
}
fetchData() {
this.abortController = new AbortController();
fetch(`/api/data/${this.props.id}`, {
signal: this.abortController.signal
})
.then(response => response.json())
.then(data => this.setState({ data }))
.catch(error => console.error('Error:', error));
}
render() {
return (
<div>
<h1>Count: {this.state.count}</h1>
<p>Props: {this.props.id}</p>
</div>
);
}
}Hooks 生命周期示例
javascriptCode
function LifecycleComponent({ id }) {
const [count, setCount] = React.useState(0);
const [data, setData] = React.useState(null);
// 挂载和更新
React.useEffect(() => {
console.log('Effect ran');
// 发起网络请求
const abortController = new AbortController();
fetch(`/api/data/${id}`, {
signal: abortController.signal
})
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.error('Error:', error));
// 清理函数(卸载或依赖变化时执行)
return () => {
console.log('Cleanup');
abortController.abort();
};
}, [id]); // 依赖数组
// 挂载
React.useEffect(() => {
console.log('Component mounted');
// 设置定时器
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// 清理定时器
return () => clearInterval(timer);
}, []); // 空依赖数组,只在挂载时执行
return (
<div>
<h1>Count: {count}</h1>
<p>Props: {id}</p>
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}最佳实践
•使用函数组件和 Hooks 代替 class 组件
•合理使用 state 和 props,避免不必要的渲染
•使用 key 属性提高列表渲染性能
•组件拆分要合理,保持单一职责原则
•使用 React.memo 优化组件渲染
•使用 useMemo 和 useCallback 优化性能
•避免在渲染中创建新对象和函数
•合理使用 Context API