JSX 是 JavaScript 的一种语法扩展,全称叫做( JavaScript XML),允许你在 JavaScript 代码中编写类似 HTML 的结构。它本质上是 React.createElement(component, props, ...children)调用的语法糖,最终会被编译成用于描述 UI 的 JavaScript 对象,而这个用于描述 UI 的 JavaScript 对象便是我们所提起的虚拟DOM。
源码解析
// 编写的JSX
const element = <div className="container">Hello</div>;
// 实际的 JavaScript,调用 React.createElement函数
const element = React.createElement(
'div',
{ className: 'container' },
'Hello'
);
// React.createElement 返回的对象
{
$$typeof: Symbol(react.element),
type: 'div', //标签类型
key: null, //key,列表渲染优化, 这也是为什么react的jsx有key这个属性,而原生html标签却没有
ref: null, // dom引用
props: {
className: 'container', //样式
children: 'Hello' //innerHTML的信息
},
_owner: null,
_store: {}
}
JSX 编译过程
JSX 代码 → Babel/Parser → AST → transform → JavaScript 代码
JSX编译与虚拟DOM创建(render)
JSX到虚拟DOM的完整流程
JSX代码 → Babel解析 → AST抽象语法树 → JSX转换 → React.createElement调用 → 虚拟DOM对象
Babel转换的两种模式
// (React 16及之前)
// JSX: <div>Hello</div>
// 转换为:
React.createElement('div', null, 'Hello')
// (React 17+)
// JSX: <div>Hello</div>
// 转换为:
import { jsx as _jsx } from 'react/jsx-runtime'
_jsx('div', { children: 'Hello' })
完整的虚拟DOM对象结构
// React.createElement返回的完整虚拟DOM对象
const vdom = {
// 唯一标识React元素类型
$$typeof: Symbol.for('react.element'),
// 元素类型
type: 'div', // 可以是字符串、函数组件、类组件、Fragment等
// React内部使用的唯一标识
_owner: null,
_store: {},
_self: null,
_source: null,
// 关键属性
key: null, // 列表渲染优化
ref: null, // DOM引用
// 所有属性(包括children)
props: {
className: 'container',
style: { color: 'red' },
children: [
'Hello',
{ $$typeof: Symbol.for('react.element'), type: 'span', ... }
]
}
}
虚拟DOM到真实DOM的渲染流程(commit)
完整渲染流程图
虚拟DOM树 → ReactDOM.render() →
├── 协调阶段 (Render)
│ ├── 创建Fiber树
│ ├── 构建工作单元
│ └── 执行Diff算法
│
├── 提交阶段 (Commit)
│ ├── 更新前准备
│ ├── DOM操作执行
│ └── 生命周期调用
│
└── 真实DOM更新
详细渲染步骤
步骤1:初始化渲染
// 1. ReactDOM.render调用
ReactDOM.render(
<App />, // JSX元素(虚拟DOM)
document.getElementById('root') // 挂载点
);
// 2. 创建Fiber根节点
function createFiberRoot(containerInfo) {
return {
current: null, // 当前渲染的Fiber树
containerInfo: containerInfo, // DOM容器
pendingChildren: null,
// ... 其他内部属性
};
}
// 3. 开始渲染流程
function scheduleUpdateOnFiber(fiber, lane) {
// 调度更新,进入React调度系统
ensureRootIsScheduled(root);
}
步骤2:协调阶段(Reconciliation)
// Fiber节点结构
const fiberNode = {
tag: WorkTag, // 组件类型(函数组件、类组件、宿主组件等)
key: null, // 唯一标识
elementType: Function, // 组件函数/类
type: 'div', // DOM标签名
stateNode: null, // 对应的真实DOM节点
return: parentFiber, // 父Fiber
child: childFiber, // 第一个子Fiber
sibling: nextFiber, // 下一个兄弟Fiber
pendingProps: props, // 新属性
memoizedProps: props, // 上次渲染的属性
memoizedState: state, // 状态
updateQueue: null, // 更新队列
// ... 其他Fiber属性
};
// 构建Fiber树的入口函数
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// 处理单个Fiber节点
function performUnitOfWork(unitOfWork) {
const current = unitOfWork.alternate;
// beginWork阶段:创建子Fiber节点
let next = beginWork(current, unitOfWork, renderLanes);
if (next === null) {
// completeWork阶段:完成当前节点
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
// beginWork:根据组件类型执行不同逻辑
function beginWork(current, workInProgress, renderLanes) {
switch (workInProgress.tag) {
case FunctionComponent:
return updateFunctionComponent(
current, workInProgress, workInProgress.type, renderLanes
);
case ClassComponent:
return updateClassComponent(
current, workInProgress, workInProgress.type, renderLanes
);
case HostComponent: // DOM元素
return updateHostComponent(current, workInProgress, renderLanes);
case HostText: // 文本节点
return updateHostText(current, workInProgress);
// ... 其他类型
}
}
// Diff算法核心(reconcileChildren)
function reconcileChildren(current, workInProgress, nextChildren) {
if (current === null) {
// 首次渲染:直接插入
workInProgress.child = mountChildFibers(
workInProgress,
null,
nextChildren,
renderLanes
);
} else {
// 更新:Diff比较
workInProgress.child = reconcileChildFibers(
workInProgress,
current.child,
nextChildren,
renderLanes
);
}
}
// Diff算法策略
function reconcileSingleElement(returnFiber, currentFirstChild, element) {
const key = element.key;
let child = currentFirstChild;
while (child !== null) {
// 1. 比较key是否相同
if (child.key === key) {
// 2. 比较type是否相同
if (child.elementType === element.type) {
// 复用节点
deleteRemainingChildren(returnFiber, child.sibling);
const existing = useFiber(child, element.props);
existing.return = returnFiber;
return existing;
}
// key相同但type不同,删除旧节点
deleteRemainingChildren(returnFiber, child);
break;
}
child = child.sibling;
}
// 创建新节点
const created = createFiberFromElement(element);
created.return = returnFiber;
return created;
}
步骤3:提交阶段(Commit)
// 提交阶段入口
function commitRoot(root) {
const finishedWork = root.finishedWork;
// 阶段1:DOM更新前(调用getSnapshotBeforeUpdate)
commitBeforeMutationEffects();
// 阶段2:DOM更新
commitMutationEffects(root, renderPriorityLevel);
// 切换当前Fiber树
root.current = finishedWork;
// 阶段3:DOM更新后
commitLayoutEffects(finishedWork, root, lanes);
}
// DOM创建函数
function commitPlacement(finishedWork) {
const parentFiber = getHostParentFiber(finishedWork);
const parent = parentFiber.stateNode;
// 获取插入位置
const before = getHostSibling(finishedWork);
if (parent.tag === HostComponent) {
// 插入DOM节点
insertOrAppendPlacementNode(finishedWork, before, parent);
}
}
// 具体的DOM操作方法
function createInstance(type, props, rootContainerInstance) {
const domElement = document.createElement(type);
// 设置属性
updateProperties(domElement, type, {}, props);
// 设置初始属性值
setInitialProperties(domElement, type, props);
return domElement;
}
// 属性更新
function updateProperties(domElement, tag, lastProps, nextProps) {
for (let propKey in nextProps) {
const nextProp = nextProps[propKey];
const lastProp = lastProps[propKey];
if (propKey === 'children') {
if (typeof nextProp === 'string' || typeof nextProp === 'number') {
setTextContent(domElement, nextProp);
}
} else if (propKey === 'style') {
setValueForStyles(domElement, nextProp, lastProp);
} else if (isCustomProp(propKey)) {
// 自定义属性处理
} else if (propKey === 'dangerouslySetInnerHTML') {
// 危险HTML设置
} else if (shouldIgnoreAttribute(propKey)) {
// 忽略的属性
} else if (isEventProp(propKey)) {
// 事件处理
updateEventListener(domElement, propKey, lastProp, nextProp);
} else {
// 普通属性
setValueForProperty(domElement, propKey, nextProp, tag);
}
}
}
步骤4:生命周期与副作用调用
// 类组件生命周期调用
function commitLifeCycles(finishedRoot, current, finishedWork) {
switch (finishedWork.tag) {
case ClassComponent:
const instance = finishedWork.stateNode;
if (current === null) {
// 首次挂载
instance.componentDidMount();
} else {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
// 更新
instance.componentDidUpdate(prevProps, prevState);
}
break;
case HostRoot:
// 根组件更新完成
flushPassiveEffects();
break;
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent:
// 函数组件的副作用
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
break;
}
}
// Hooks副作用处理
function commitHookEffectListMount(tag, finishedWork) {
const updateQueue = finishedWork.updateQueue;
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
const firstEffect = lastEffect.next;
let effect = firstEffect;
do {
if ((effect.tag & tag) === tag) {
// 执行useEffect回调
const create = effect.create;
const destroy = create();
effect.destroy = destroy;
}
effect = effect.next;
} while (effect !== firstEffect);
}
}