JSX本质是什么
JSX的本质
JSX (JavaScript XML) 是 JavaScript 的语法扩展,它的本质是:
- 语法糖:JSX不是有效的JavaScript代码,最终会被编译为标准的JavaScript代码
- 函数调用:JSX会被编译为React.createElement()函数调用(或类似函数)
- 描述 UI 的结构:提供了一种声明式的方式描述用户界面
原始JSX代码:
const element = <h1 className="title">Hello, world!</h1>;
编译后的JavaScript代码
const element = React.createElement('h1', // 标签名{ className: 'title' }, // 属性对象'Hello, world!' // 子元素
);
JSX转换为真实DOM的过程
1、编译阶段(Babel)处理
工具:Babel+@babel/preset-react
作用:将JSX转化为React.createElement调用
原代码:
<div className="container"><Button color="blue">Click</Button>
</div>
转化后代码:
React.createElement('div',{ className: 'container' },React.createElement(Button,{ color: 'blue' },'Click')
)
2、createElement() 调用会返回一个虚拟DOM节点
这是一个普通的JavaScript对象,描述DOM的真实样式
如下:
{type: 'div',props: {className: 'container',children: {type: Button,props: {color: 'blue',children: 'Click'}}}
}
3、渲染阶段(ReactDOM处理)
- 首次渲染:ReactDOM.render()或根组件的render()
- 更新渲染:状态或属性变化时
具体过程:
1、创建虚拟DOM树:React根据组件创建完整的虚拟DOM树
2、协调(Reconciliation)
- 比较新旧DOM差异(Diff算法)
- 找出需要更新的最小变化集
3、提交(commit) - 将变化应用到真实DOM上
- 执行生命周期方法(如 componentDidMount或useEffect)
具体DOM操作:
创建节点:document.createElement()
设置属性:element.setAttribute()
添加子节点:parent.appendChild()
更新节点:精细的 DOM 操作而非整体替换
为什么将变化应用到DOM树上之后 还要执行生命周期
1、当生命周期(如componentDidMount 、componentDidUpdate )或 useEffect 触发时,react保证:
- DOM已实际更新完成:可以安全地执行依赖于 DOM 的操作(如测量元素尺寸、调用第三方库初始化等)
- 避免“半成品”状态:如果先执行生命周期再更新 DOM,开发者可能会看到不一致的 UI 状态。
componentDidMount() {// 此时 div 已挂载到真实 DOM,可以正确获取宽度const width = this.divElement.clientWidth;
}render() {return <div ref={el => this.divElement = el} />;
}
2、副作用执行的正确时机
React 将副作用(如数据获取、订阅)分为三个阶段:
1、render阶段:计算虚拟DOM差异(可中断,无副作用)
2、commit阶段:
- Before Mutation:执行getSnapshotBeforeUpdate(获取 DOM 更新前的快照)
- Mutation:将变更应用到真实DOM上
- Layout:同步执行 componentDidMount/Update 和 useLayoutEffect(此时 DOM 已更新,但浏览器尚未绘制)
3、Paint阶段:浏览器绘制后,异步执行useEffect
这种分离保证了:
- 同步紧急操作(如 DOM 测量)在 useLayoutEffect 中立即执行。
- 非紧急操作(如数据请求)在 useEffect 中异步执行,避免阻塞渲染。
3、与 Fiber 架构的协调机制配合
React Fiber 的调度机制需要:
- 可中断的 Render 阶段:虚拟 DOM 计算可被打断,但必须保证提交到 DOM 的变更是一次性、不可中断的。
- 批量更新优化:多个状态更新可能合并为一次 DOM 修改,生命周期在最终提交后统一触发
// React 的提交阶段伪代码
function commitRoot() {// 1. 应用 DOM 变更commitMutationEffects();// 2. 同步执行生命周期/布局 EffectcommitLayoutEffects(); // componentDidMount/Update, useLayoutEffect// 3. 标记任务完成,安排异步 EffectschedulePassiveEffects(); // useEffect
}
4、错误边界与回滚机制
如果在生命周期或 Effect 中抛出错误:
- React 可以捕获错误并显示备用 UI(通过 Error Boundary)。
- DOM 已处于一致状态,不会留下部分更新的界面。
总结:React 的生命周期/Effect 执行顺序设计
阶段 | 执行内容 | 特点 |
---|---|---|
Before Mutation | getSnapshotBeforeUpdate | 获取更新前 DOM 状态 |
Mutation | 应用 DOM 变更 | 实际修改真实 DOM |
Layout | componentDidMount/Update、useLayoutEffect | 同步执行,DOM 已更新 |
Paint | useEffect | 异步执行,浏览器已绘制 |
这种设计保证了:
- 可靠性:DOM操作后生命周期操作安全访问DOM
- 性能:将副作用分类处理,避免阻塞渲染
- 一致性:确保UI和数据同步