目录
(1) 从 ReactElement 到 FiberNode
(4)DOM节点有直接的属性-关联到此dom对应的fiber节点对象属性
react.js函数开头
///执行输出dffff
(
//factory 就是第二个匿名函数 function(){console.log("dffff")}
function (global, factory)
{
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.React = {}));
}(
this,
function(){console.log("dffff")}//这个就是具体被执行的函数
)
);
这是一个立即执行函数表达式(Immediately Invoked Function Expression, IIFE)中的一种变体
还有一个种是: (function(){console.log("dfdf")})()
相关js文件介绍
react的cdn相关文件
<!-- 非压缩的 React 和 ReactDOM -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel(压缩版,非压缩版可能不可用) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
React.js 和 React-DOM.js 都是 React 库的重要组成部分,但它们各自扮演着不同的角色:
React.js:这个库主要包含了 React 的核心功能,比如组件的声明周期、状态管理(State)和属性(Props)等。它提供了一种描述 UI 界面的方法,但并不关心这些界面是如何渲染到页面上的。这意味着你可以使用 React 来定义用户界面,而无需考虑其最终会运行在什么平台上(例如,浏览器或移动设备)。
ReactDOM.js:这个库则专门用于将 React 组件渲染到 DOM 中,提供了针对浏览器环境的具体实现。它包括了像 ReactDOM.render() 这样的方法,用于将你的 React 组件实际显示在网页上。此外,它还负责处理事件分发等与DOM交互的细节。简单来说,ReactDOM 是 React 与浏览器之间的桥梁。
因此,如果你正在开发一个 Web 应用程序,你通常需要同时包含 React.js 和 ReactDOM.js。而对于非 DOM 的环境(如 React Native),你不需要 ReactDOM,因为这些平台有自己的方式来“渲染”组件。在这些情况下,React 核心库依然用来定义组件和管理状态,但是渲染过程由特定于平台的库来处理。
实例代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React via CDN (非压缩版)</title>
</head>
<body>
<div id="root"></div>
<!-- 非压缩的 React 和 ReactDOM -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel(压缩版,非压缩版可能不可用) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- React 代码 -->
<script type="text/babel">
function App() {
return (
<div>
<h1>非压缩版 React</h1>
<p>使用开发版本 CDN 文件。</p>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>
在上面 <script type="text/babel"></script>中的代码浏览器并不认识,需要 babel编译之后,变成react的相关api代码,才能运行
函数组件实例代码
源代码
<script type="text/babel">
function App() {
//按顺序在当前组件的FiberNode中形成一个memoizedState单向链表
const [title, setTitle] = React.useState("Class Component444 Demo");
React.useEffect(() => {console.log('Component title');}, [title]);
const [appName, setAppName] = React.useState("personname");
React.useEffect(() => {console.log('Component appName');}, [appName]);
const [desc, setDesc] = React.useState("我是谁");
React.useEffect(() => {console.log('Component desc');}, [desc]);
return (
<div>
<h1>非压缩版 React</h1>
<p>{appName}</p>
<p>{title}</p>
<button onClick={() => setTitle("Clas00000000000 Demo")}>修改标题</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
//编译之后的代码: root.render(React.createElement(App, { title: "Class Component Demo" }));
//App 是函数组件 编译之后的函数
root.render(<App title="Class Component Demo" />);
</script>
编译之后代码
""use strict";
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
function App() {
//按顺序在当前组件的FiberNode中形成一个memoizedState单向链表
var _React$useState = React.useState("Class Component444 Demo"),
_React$useState2 = _slicedToArray(_React$useState, 2),
title = _React$useState2[0],
setTitle = _React$useState2[1];
React.useEffect(function () {
console.log('Component title');
}, [title]);
var _React$useState3 = React.useState("personname"),
_React$useState4 = _slicedToArray(_React$useState3, 2),
appName = _React$useState4[0],
setAppName = _React$useState4[1];
React.useEffect(function () {
console.log('Component appName');
}, [appName]);
var _React$useState5 = React.useState("我是谁"),
_React$useState6 = _slicedToArray(_React$useState5, 2),
desc = _React$useState6[0],
setDesc = _React$useState6[1];
React.useEffect(function () {
console.log('Component desc');
}, [desc]);
return React.createElement(
"div",
null,
React.createElement(
"h1",
null,
"\u975E\u538B\u7F29\u7248 React"
),
React.createElement(
"p",
null,
appName
),
React.createElement(
"p",
null,
title
),
React.createElement(
"button",
{ onClick: function onClick() {
return setTitle("Clas00000000000 Demo");
} },
"\u4FEE\u6539\u6807\u9898"
)
);
}
var root = ReactDOM.createRoot(document.getElementById('root'));
//编译之后的代码: root.render(React.createElement(App, { title: "Class Component Demo" }));
//App 是函数组件 编译之后的函数
root.render(React.createElement(App, { title: "Class Component Demo" }));
Babel和React怎么结合
react 和 babel是怎么结合的? 在浏览器中 babel 把 <script type="text/babel"></script>中的内容编译之后,是怎么处理编译之后的结果的? 会把结果直接给react吗? 还是直接执行编译的结果?
React 和 Babel 的结合是通过 Babel 的实时编译能力实现的,特别是在浏览器环境中使用 <script type="text/babel">
时。以下是详细的工作流程:
1. React 和 Babel 的角色
-
React:负责构建 UI 组件、管理虚拟 DOM 和渲染逻辑。
-
Babel:将 JSX 语法(或高级 JavaScript 语法)转换为浏览器能理解的普通 JavaScript。
2. 浏览器中的结合流程
步骤 1:引入依赖
<!-- 1. 引入 React 和 ReactDOM -->
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- 2. 引入 Babel Standalone(浏览器端编译 JSX) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 3. 编写 JSX -->
<script type="text/babel">
function App() {
return <h1>Hello, React + Babel!</h1>;
}
ReactDOM.createRoot(document.getElementById("root"))
.render(<App />);
</script>
步骤 2:Babel 的编译过程
-
检测
<script type="text/babel">
Babel 的babel-standalone
库会监听页面加载事件,扫描所有type="text/babel"
的脚本标签。 -
实时编译 JSX
Babel 将 JSX 转换为React.createElement
的调用形式。例如:// 转换前的 JSX <h1>Hello</h1> // 转换后的 JavaScript React.createElement('h1', null, 'Hello');
-
插入编译后的代码
Babel 会将编译后的代码动态插入到页面中,生成一个新的<script>
标签,并替换原始的<script type="text/babel">
内容。例如:<script> function App() { return React.createElement('h1', null, 'Hello, React + Babel!'); } ReactDOM.createRoot(document.getElementById("root")) .render(React.createElement(App)); </script>
步骤 3:执行编译后的代码
-
编译后的代码是标准的 JavaScript,浏览器会直接执行它。
-
执行过程中,代码调用
React.createElement
和ReactDOM.render
,触发 React 的渲染逻辑。 -
React 最终将组件渲染到指定的 DOM 容器(如
id="root"
的<div>
)。
3. 关键问题解答
Babel 编译后的代码是传给 React,还是直接执行?
-
直接执行编译后的代码。
Babel 的编译结果是纯 JavaScript,浏览器直接运行这些代码,代码中会调用 React 的 API(如React.createElement
和ReactDOM.render
)。
React 如何感知到编译后的代码?
-
React 不需要主动感知。
编译后的代码直接通过全局变量React
和ReactDOM
(由 CDN 脚本引入)调用 React 的 API。例如:// 编译后的代码显式调用 React 的方法 React.createElement(...); ReactDOM.createRoot(...).render(...);
4. 代码执行流程示意图
原始 JSX代码
↓
Babel 编译为 React.createElement 调用(普通 JavaScript)
↓
浏览器执行编译后的代码
↓
调用 React API 创建虚拟 DOM
↓
ReactDOM 将虚拟 DOM 渲染为真实 DOM
5. 关键细节
1. Babel Standalone 的工作原理
-
babel-standalone
会在页面加载时自动运行,扫描所有type="text/babel"
的脚本。 -
它会通过
Babel.transform
方法实时编译代码,并将结果插入到页面中。
2. JSX 的依赖
-
JSX 的编译结果依赖
React
全局变量。
如果未引入 React 的 CDN 脚本,编译后的代码会报错:
Uncaught ReferenceError: React is not defined
。
3. 性能问题
-
浏览器中实时编译 JSX 性能较差,仅适用于学习和原型开发。
生产环境应通过构建工具(如 Webpack、Vite)提前编译 JSX。
6. 完整流程示例
假设原始 JSX 为:
function App() {
return (
<div className="container">
<h1>Hello</h1>
<button onClick={() => alert("Clicked!")}>Click Me</button>
</div>
);
}
Babel 会将其编译为:
function App() {
return React.createElement(
"div",
{ className: "container" },
React.createElement("h1", null, "Hello"),
React.createElement(
"button",
{ onClick: () => alert("Clicked!") },
"Click Me"
)
);
}
浏览器执行这段代码后,React 会生成虚拟 DOM,最终由 ReactDOM 渲染到页面上。
7. 总结
-
Babel 的作用:将 JSX 转换为浏览器可执行的 JavaScript。
-
React 的作用:接收编译后的代码(调用
React.createElement
),管理组件和渲染逻辑。 -
流程本质:Babel 是“翻译官”,React 是“执行者”,两者通过全局变量和标准 API 调用协作。
函数组件babel编译实例
实例1
function App() {
const [title, setTitle] = React.useState("Class Component444 Demo");
return (
<div>
<h1>React</h1>
<p>CDN-Version</p>
<p>{title}</p>
<button onClick={() => setTitle("Clas00000000000 Demo")}>修改标题</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App title="Class Component Demo" />);
编译之后
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
function App() {
const [title, setTitle] = React.useState("Class Component444 Demo");
return /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("h1", {
children: "React"
}), /*#__PURE__*/_jsx("p", {
children: "CDN-Version"
}), /*#__PURE__*/_jsx("p", {
children: title ///是在当前函数组件的fiber中
}), /*#__PURE__*/_jsx("button", {
onClick: () => setTitle("Clas00000000000 Demo"),
children: "\u4FEE\u6539\u6807\u9898"
})]
});
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(/*#__PURE__*/_jsx(App, {
title: "Class Component Demo"
}));
调试入口--在createRoot$1方法断点
下面几个createElement方法调用你的先后顺序。
最外部的createElement 最后调用,然后就是几个参数 的对象从左到右执行createElement,也就是先创建h1 -> p ->-p->button
React重要对象
类组件实例
class Counter extends React.Component {
// 构造函数(初始化 state 和绑定方法)
constructor(props) {
super(props); // 必须调用 super(props)
this.comInputRef=React.createRef();
this.state = { inputValue: '',count: 0 };
}
// 自定义方法(更新 state)
handleClick() {
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
// window.comp1=this;
//console.log(this.comInputRef.current,this.comInputRef.current.value);
}
handleChange(event) {
console.log(event.target.value);
this.setState({ count: event.target.value });
}
// 生命周期方法(可选)
componentDidMount() {
console.log('Component mounted');
}
// 必须实现 render()
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>Count: {this.state.count}</p>
{/* comInputRef.current 就是input这个dom对象 */}
<input type="text" ref={this.comInputRef} onChange={(evt)=>{this.handleChange(evt);}} />
<button onClick={()=>{this.handleClick()}}>Increment</button>
</div>
);
}
}
组件实例有一个属性: _reactInternals指向这个组件对应的 Fiber节点,FiberNode对象。
FiberNode对象
说明
-
定义:React 内部用于协调(Reconciliation)的数据结构,代表一个待处理的工作单元。
-
特点:
-
构成 Fiber 树(React 的异步渲染和优先级调度的基础)。
-
包含组件状态、副作用(如生命周期、DOM 操作)、父子兄弟节点关系等。
-
每个 ReactElement 对应一个或多个 FiberNode(取决于组件层级)。
-
关键属性:
interface FiberNode {
tag: WorkTag; // 标识 Fiber 类型(如 FunctionComponent, ClassComponent)
type: any; // 对应的 ReactElement 的 type(组件或 DOM 标签)
stateNode: any; // 关联的组件实例或 DOM 节点
return: FiberNode | null; // 父 Fiber
child: FiberNode | null; // 第一个子 Fiber
sibling: FiberNode | null; // 下一个兄弟 Fiber
memoizedProps: any; // 上一次渲染的 props
memoizedState: any; // 上一次渲染的 state(如 Hooks 链表)
// ... 其他调度相关属性(如优先级、副作用标记)
}
如果FiberNode的节点是一个普通 html标签,那么这个属性stateNode就是一个dom对象,
document.getElementsByTagName("div")[1]===window.CounterInstan(组件构造函数中赋值的)._reactInternals.child.stateNode
组件实例和FiberNode节点对象是相互引用的。
ReactElement
这个对象是每个节点对应的虚拟Dom。是在组件render 时创建的,调用React.createElement(type,props,child) 函数创建的。描述 UI 的结构(如组件类型、属性、子元素)
为什么需要 ReactElement?
-
轻量描述: 用普通对象描述 UI,避免直接操作 DOM。
-
协调优化: React 通过对比新旧 ReactElement 对象(Diff 算法)决定如何高效更新 DOM。
-
跨平台: ReactElement 是平台无关的抽象层,支持 Web、Native 等渲染目标。
const element = {
// 核心标识
$$typeof: Symbol.for('react.element'), // 标识这是一个 React 元素
type: 'div' | ComponentType, // 元素的类型(字符串、函数或类组件)
key: string | null, // 唯一标识,用于列表优化
ref: string | RefObject | null, // 指向 DOM 或组件实例的引用
props: { // 元素的属性集合
children: ReactElement | ReactElement[] | string | number | null,
// 其他自定义属性(如 className、onClick 等)
},
_owner: Fiber | null, // 指向创建该元素的 Fiber 节点(内部使用)
};
属性type
-
作用: 定义元素的类型,决定如何渲染。
-
可能的类型:
-
字符串: 对应原生 DOM 元素(如
'div'
,'span'
)。 -
函数/类组件: 自定义组件(如
MyComponent
)。 -
React 特殊类型:
Fragment
,Suspense
等。
-
// 原生元素
{ type: 'button', props: { children: 'Click' } }
// 函数组件
function Button() { return <button>OK</button> }
{ type: Button, props: {} }
属性key
-
作用: 唯一标识列表中的元素,优化 React 的 Diff 算法。
-
规则:
-
唯一性:同一列表中的兄弟元素必须唯一。
-
稳定性:避免使用随机值(如
Math.random()
)。
-
// 列表渲染
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
属性ref
-
作用: 获取对 DOM 节点或组件实例的引用。
-
使用方式:
-
字符串(已弃用):
ref="myRef"
。 -
回调函数:
ref={(node) => { this.node = node }}
。 -
useRef
Hook:const ref = useRef(null); <div ref={ref} />
。
-
-
示例:
// 类组件中使用 ref class MyComponent extends React.Component { componentDidMount() { this.inputRef.focus(); // 访问 DOM 节点 } render() { return <input ref={(el) => (this.inputRef = el)} />; } }
属性props
-
作用: 包含元素的属性和子元素。
-
特殊属性:
-
children
: 子元素(可以是单个元素、数组或文本)。 -
className
: 对应 DOM 的class
。 -
style
: 内联样式(对象格式,如{ color: 'red' }
)。 -
事件处理器:如
onClick
,onChange
。
-
-
示例:
// JSX: <div className="box" onClick={handleClick}>Hello</div> { type: 'div', props: { className: 'box', onClick: handleClick, children: 'Hello' } }
_owner
(内部属性) -
作用: 指向创建该元素的 Fiber 节点(React 内部协调算法使用)。
-
开发者用途: 通常无需直接操作,但可通过 React 开发工具追踪组件层级。
三者的关系
(1) 从 ReactElement 到 FiberNode
-
初始渲染:
-
React 根据 ReactElement 树生成 Fiber 树。
-
每个 ReactElement 对应一个 FiberNode(若元素是组件,可能递归生成子 Fiber)。
-
-
更新时:
-
React 对比新旧 ReactElement,更新对应的 FiberNode。
-
FiberNode 保存组件状态和 DOM 节点的引用,使得更新可中断和恢复。
-
(2) 从 FiberNode 到组件实例
-
类组件:
-
FiberNode 的
stateNode
属性指向类组件的实例。
// FiberNode 结构示例(类组件) { tag: ClassComponent, type: Button, // 组件类 stateNode: instance, // Button 的实例 // ... }
-
-
函数组件:
-
函数组件没有实例,但通过
useState
等 Hooks 将状态存储在 FiberNode 的memoizedState
中。
-
(3) 从组件实例到 DOM 节点
-
类组件:
-
组件实例可通过
this.refs
或React.createRef()
获取子组件或 DOM 节点的引用。
-
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
// 通过 this.myRef.current 访问 DOM 节点
(4)DOM节点有直接的属性-关联到此dom对应的fiber节点对象属性
如何获取引用?
(1) 开发者手动获取
-
ReactElement → DOM 节点:
-
使用
ref
属性绑定 DOM 或类组件实例。
function MyComponent() { const divRef = useRef(null); return <div ref={divRef}>Hello</div>; } // divRef.current 指向 DOM 节点
-
-
组件实例 → 子组件实例:
-
通过
ref
获取子类组件的实例。
class Parent extends React.Component { childRef = React.createRef(); render() { return <Child ref={this.childRef} />; } } // this.childRef.current 指向 Child 的实例
-
///上面组件编译之后的代码
class Parent extends React.Component {
constructor(...args) {
super(...args);
_defineProperty(this, "childRef", React.createRef());
}
render() {
//this.childRef 作为参数传给了 创建子元素ReactElement的方法中,创建好的子元素ReactElement实例会赋值给 ref 也就是this.childRef,这样父类就可以拿到子类 虚拟Dom的ReactElement实例
return /*#__PURE__*/_jsx(Child, {
ref: this.childRef
});
}
}
(2) React 内部引用
-
FiberNode → 组件实例:
-
类组件的 FiberNode 的
stateNode
直接指向实例。
// 内部 React 代码(简化) function updateClassComponent(fiber) { const instance = new fiber.type(fiber.props); fiber.stateNode = instance; // 关联实例 }
-
-
FiberNode → DOM 节点:
-
宿主组件(如
div
)的 FiberNode 的stateNode
指向 DOM 节点。
// 内部 React 代码(简化) function createDOMElement(fiber) { const dom = document.createElement(fiber.type); fiber.stateNode = dom; // 关联 DOM return dom; }
-
-
组件实例 → FiberNode:
-
通过 React 内部方法(如
__reactInternalInstance$<key>
,非公开 API,仅调试用)。
// 仅用于理解原理(实际不可靠!) const instance = document.getElementById('my-component'); const fiber = instance._reactInternalFiber;
-
4. 关系图
JSX → ReactElement Tree → Fiber Tree → DOM
| |
| → Component Instance (类组件)
→ Refs → DOM/Component Instance
5. 关键总结
概念 | 作用 | 开发者访问方式 | 内部引用关系 |
---|---|---|---|
ReactElement | 描述 UI 结构 | JSX、React.createElement | 被 FiberNode 的 type 和 props 引用 |
FiberNode | 协调、调度、状态管理 | 不可直接访问 | 通过 stateNode 关联组件实例或 DOM |
组件实例(类组件) | 保存状态、生命周期 | this 、ref.current | FiberNode 的 stateNode 属性 |
6. 实际场景示例
场景:父组件调用子组件方法
class Child extends React.Component {
doSomething() {
console.log('Child method called');
}
render() {
return <div>Child</div>;
}
}
class Parent extends React.Component {
childRef = React.createRef();
handleClick = () => {
this.childRef.current.doSomething(); // 通过 ref 调用子组件实例方法
};
render() {
return (
<div>
<Child ref={this.childRef} />
<button onClick={this.handleClick}>Call Child</button>
</div>
);
}
}
内部流程:
-
Parent
渲染时,为Child
创建 ReactElement。 -
React 生成对应的 FiberNode,其
stateNode
指向Child
实例。 -
Parent
通过ref
绑定到Child
实例,存储在childRef.current
。 -
点击按钮时,通过
childRef.current
直接调用子组件方法。
注意事项
-
避免滥用 ref:大多数情况下应通过 Props 控制子组件,而非直接操作实例。
-
函数组件无实例:若需暴露方法,需使用
useImperativeHandle
。 -
FiberNode 是内部实现:直接操作 FiberNode 可能导致版本升级不兼容。
FiberRootNode对象
类似vue3中的app对象,一个应用就这一个FiberRootNode,是有ReactDOM.createRoot()函数创建const root = ReactDOM.createRoot(document.getElementById('root'));。类似对应根挂载点的FiberNode。
FiberRootNode有一个属性current指向 挂载点的第一个子组件的FiberNode实例对象
FiberNode的memoizedState熟悉对象
如下函数组件
function App() {
//按顺序在当前组件的FiberNode中形成一个memoizedState单向链表
const [title, setTitle] = React.useState("Class Component444 Demo");
React.useEffect(() => {console.log('Component title');}, [title]);
const [appName, setAppName] = React.useState("personname");
React.useEffect(() => {console.log('Component appName');}, [appName]);
const [desc, setDesc] = React.useState("我是谁");
React.useEffect(() => {console.log('Component desc');}, [desc]);
return (
<div>
<h1>非压缩版 React</h1>
<p>{appName}</p>
<p>{title}</p>
<button onClick={() => setTitle("Clas00000000000 Demo")}>修改标题</button>
</div>
);
}
var hook = {
//当前状态' 如 useState 的值 / useEffect 的 effect 对象
memoizedState: null,
//基础状态
baseState: null,
///未处理的更新队列
baseQueue: null,
//更新队列 存储 setState 的更新对象
queue: null,
//指向下一个 Hook
next: null
};
上面组件代码会形成一个连表
FiberNode的memoizedState指向 title的hook对象,然后title的hook的
更新任务对象update
通过此方法创建
function createUpdate(eventTime, lane) {
var update = {
eventTime: eventTime,
lane: lane,
tag: UpdateState,
payload: null,// 更新所携带的数据
callback: null,
next: null
};
return update;
}
这是 React 更新机制的核心数据结构。每个属性都有特定的作用:
各属性详解:
-
eventTime
(事件时间)-
作用:记录更新创建时的时间戳(performance.now())
-
用途:用于计算更新优先级和过期时间,帮助 React 判断哪些更新需要优先处理
-
示例值:
18392.25
(高精度时间戳)
-
-
lane
(车道)-
作用:表示更新的优先级类型(基于 React 的并发模型)
-
用途:
-
决定更新处理的紧急程度(如用户交互 vs 后台数据加载)
-
实现更新批处理和优先级插队
-
-
类型:位掩码(如
SyncLane = 1
,InputContinuousLane = 4
) -
特点:React 17+ 引入的并发模式核心机制
-
-
tag
(类型标记)-
作用:标识更新的类型
-
常见取值:
-
UpdateState
(0):常规状态更新(如 setState) -
ReplaceState
(1):替换状态(类组件) -
ForceUpdate
(2):强制更新(shouldComponentUpdate 绕过) -
CaptureUpdate
(3):错误边界捕获的更新
-
-
用途:决定如何处理 payload 中的内容
-
-
payload
(更新载荷)-
作用:携带实际的更新数据
-
内容取决于场景:
-
类组件:包含
{ key: value }
状态对象 或(prevState) => newState
函数 -
函数组件:useState 的新值 或 useReducer 的 action
-
HostRoot:ReactDOM.render 的根组件
-
-
特殊值:可能为
null
(如强制更新时不需要新状态)
-
-
callback
(回调函数)-
作用:更新提交后执行的回调(如 setState 的第二个参数)
-
特点:
-
在 commit 阶段执行(此时 DOM 已更新)
-
多个更新共享回调时会合并执行
-
-
示例:
() => console.log('Updated!')
-
-
next
(链表指针)-
作用:指向队列中的下一个更新对象
-
数据结构:形成环形单向链表
-
更新队列示例:
// 更新队列结构 queue: { baseState: {...}, // 当前状态 firstBaseUpdate: update1, // 队列头 lastBaseUpdate: update3, // 队列尾 shared: { pending: update4 } // 新添加的更新 } 更新链表:update1 → update2 → update3 → update4 → (循环回头)
-
工作流程示例:
// 用户调用 setState
setState({ count: 1 }, callback)
// React 内部创建更新对象
const update = {
eventTime: performance.now(),
lane: InputContinuousLane, // 用户交互高优先级
tag: UpdateState,
payload: { count: 1 }, // 新状态
callback: callback,
next: null
}
// 添加到 Fiber 的更新队列
fiber.updateQueue.shared.pending = update;
关键特点:
-
批处理机制:连续调用 setState 会创建多个 update 对象并链接成队列
setState({ a: 1 }) // 创建 update1 setState({ b: 2 }) // 创建 update2 → update1.next = update2
-
优先级处理:高优先级更新(如用户输入)会跳过低优先级更新
-
状态计算:React 会按顺序处理链表,基于 baseState 逐步计算最终状态:
let newState = baseState; while (update) { newState = typeof payload === 'function' ? payload(newState) : Object.assign({}, newState, payload); update = update.next; }
这个更新对象是 React 实现可中断渲染、优先级调度和状态一致性的基础数据结构,虽然开发者不直接操作它,但理解其机制对优化 React 应用性能有重要意义。
*Lanes(车道集合)**:一个 Lanes 是一个二进制数,可以同时包含多个 Lane(即多个优先级)。例如,`0b00000101` 表示同时包含同步优先级(0b00000001)和默认优先级(0b00000100)。
React同步模式和并发模式
高优先级别的任务会同步执行,不通过微或者宏任务,而是在当前代码块直接执行,包括更新页面操作,比如 onclick事件等
React事件合并和事件分发机制
react没有使用原生html中的事件机制,而是把所有元素的注册的都绑定到了root节点,然后root节点上有 update队列的任务链。
具体事件怎么分发的呢
dispatchDiscreteEvent ??
事件中的多个任务(update)是怎么合并的呢?
就是把计算结果合并在一个大对象中?
React16.8版本的之后大体流程
react16.8之后的版本 整体的逻辑大体理解成以下内容: 所有的 数据更新都会封装成相关的update任务,放在任务队列,然后每个update任务有 lane来定义优先级别,然后产生任务之后 就会触发 宏MessageChannel的 postMessage消息,然后触发一个宏任务,然后在下一个事件循环时 就会触发这个宏任务,然后在宏任务中就会对任务进行合并等操作,同时每次执行任务时就会取出 优先级别高的任务先执行,然后依次执行 直到完成所有的任务 形成新的FiberNode节点树 然后commit提交 更新dom。
数据变更(setState / dispatch)
↓
封装成 update 对象(Update Object)
↓
放入 fiber 节点的 updateQueue 中
↓
触发调度(scheduleWork)
↓
根据 lane 确定优先级 → 放入任务队列(Task Queue)
↓
通过 MessageChannel 触发宏任务(postMessage) → 进入事件循环
↓
在下一个事件循环中执行任务
↓
按优先级合并 & 执行任务(performUnitOfWork)
↓
构建新的 Fiber Tree(workInProgress Tree)
↓
完成 render 阶段(生成 effect list)
↓
进入 commit 阶段 → 更新 DOM、调用生命周期钩子等
React Lanes 机制详解(AI的答案 参考)
什么是 Lanes 机制?
Lanes 是 React 并发模式的核心调度机制,它使用位掩码(bitmask) 来表示不同优先级的更新任务。这种设计允许 React 高效地管理、排序和中断多个并发更新。
核心概念
-
Lane(车道):代表一种优先级类型(如用户输入、数据加载等)
-
Lanes(车道集合):多个优先级的组合(位掩码值)
-
优先级分级(从高到低):
const SyncLane = 0b00000001; // 同步优先级(最高) const InputContinuousLane = 0b00000010; // 连续输入(如拖拽) const DefaultLane = 0b00000100; // 默认优先级(点击等) const TransitionLane = 0b00001000; // 过渡更新 const RetryLane = 0b00010000; // 重试更新 const IdleLane = 0b10000000; // 空闲优先级(最低)
工作流程(含实例)
场景描述
假设我们有一个电商应用:
function ProductPage() {
const [product, setProduct] = useState(null); // 默认优先级
const [reviews, setReviews] = useState([]); // 低优先级
const [isFavorite, setIsFavorite] = useState(false); // 用户交互优先级
// 加载产品数据(默认优先级)
useEffect(() => {
fetchProduct().then(data => setProduct(data));
}, []);
// 加载评论(低优先级)
useEffect(() => {
fetchReviews().then(data => setReviews(data));
}, []);
// 用户点击收藏(高优先级)
const handleFavorite = () => {
setIsFavorite(!isFavorite);
};
return (
<div>
<ProductDetail product={product} />
<ReviewsList reviews={reviews} />
<FavoriteButton
isFavorite={isFavorite}
onClick={handleFavorite}
/>
</div>
);
}
步骤 1: 更新触发与 Lane 分配
-
用户点击收藏按钮:
// 分配高优先级车道 const favoriteLane = InputContinuousLane; // 0b00000010
-
产品数据加载完成:
// 分配默认优先级 const productLane = DefaultLane; // 0b00000100
-
评论数据加载完成:
// 分配低优先级 const reviewsLane = IdleLane; // 0b10000000
步骤 2: Lane 合并
React 使用 mergeLanes
合并这些更新:
const allLanes = mergeLanes(
favoriteLane,
mergeLanes(productLane, reviewsLane)
);
// 位运算:0b00000010 | 0b00000100 | 0b10000000
// = 0b10000110 (十进制134)
步骤 3: 标记更新路径
// 对于每个更新源 Fiber:
function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
// 更新当前 Fiber
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
// 向上冒泡更新父节点
let parent = sourceFiber.return;
while (parent) {
parent.childLanes = mergeLanes(parent.childLanes, lane);
parent = parent.return;
}
// 返回根节点
return root;
}
步骤 4: 调度器处理合并后的 Lanes
function ensureRootIsScheduled(root) {
// 获取所有合并的 Lanes (0b10000110)
const nextLanes = getNextLanes(root);
// 提取最高优先级(最右边的1)
const highestPriorityLane = getHighestPriorityLane(nextLanes);
// = 0b00000010 (InputContinuousLane)
// 根据优先级选择调度方式
if (includesSyncLane(highestPriorityLane)) {
// 同步执行
} else {
// 计算调度器优先级
const schedulerPriority = lanePriorityToSchedulerPriority(
highestPriorityLane
);
// 调度并发任务
scheduleCallback(schedulerPriority, performConcurrentWorkOnRoot);
}
}
步骤 5: 渲染阶段处理合并 Lanes
function beginWork(current, workInProgress, renderLanes) {
// 检查是否需要处理该 Fiber
if (!shouldProcessSubtree(workInProgress, renderLanes)) {
// 跳过整个子树
return bailoutOnAlreadyFinishedWork();
}
// 处理组件...
}
function shouldProcessSubtree(fiber, renderLanes) {
// 位运算检查交集
return (fiber.childLanes & renderLanes) !== NoLanes;
}
步骤 6: 处理过程(实例推演)
-
第一轮渲染(处理高优先级):
-
当前渲染 Lanes:
InputContinuousLane (0b00000010)
-
检查 ProductPage:
childLanes = 0b10000110 // 0b10000110 & 0b00000010 = 0b00000010 (≠0) → 需要处理
-
检查 FavoriteButton:
lanes = 0b00000010 // 匹配 → 更新
-
跳过 ProductDetail 和 ReviewsList:
// ProductDetail.childLanes = 0 // ReviewsList.childLanes = 0b10000000 // 0b10000000 & 0b00000010 = 0 → 跳过
-
-
第二轮渲染(处理默认优先级):
-
渲染 Lanes:
DefaultLane (0b00000100)
-
ProductDetail 更新
-
-
第三轮渲染(处理空闲优先级):
-
渲染 Lanes:
IdleLane (0b10000000)
-
ReviewsList 更新
-
合并后的 Lanes 处理关键技术
1. 优先级提取
function getHighestPriorityLane(lanes) {
// 技巧:获取最右边的1 (最低位)
return lanes & -lanes;
// 示例: 0b10000110 & -0b10000110
// = 0b10000110 & 0b01111010 (补码)
// = 0b00000010
}
2. 跳过子树渲染
function bailoutOnAlreadyFinishedWork() {
// 克隆子节点而不处理
cloneChildFibers(current, workInProgress);
return workInProgress.child;
}
3. 车道清理
在提交阶段完成后:
function commitRootImpl(root) {
// 清除已处理的 Lanes
const remainingLanes = root.pendingLanes & ~renderedLanes;
root.pendingLanes = remainingLanes;
// 更新每个 Fiber 的 lanes
let fiber = root.current;
while (fiber) {
fiber.lanes = fiber.lanes & remainingLanes;
fiber.childLanes = fiber.childLanes & remainingLanes;
fiber = fiber.sibling;
}
}
Lanes 机制的优势
-
精确优先级控制:
// 用户输入总是优先 const userInteractionPriority = InputContinuousLane | DiscreteLane;
-
高效子树跳过:
// 平均减少 50%+ 的渲染组件数
-
可中断渲染:
// 高优先级更新可以中断低优先级渲染 if (currentTaskPriority < newPriority) { interruptCurrentWork(); }
-
批处理优化:
// 合并相同优先级的更新 const batchLanes = DefaultLane | TransitionLane;
性能对比(传统 vs Lanes)
指标 | 传统堆栈调和 | Lanes 机制 | 提升 |
---|---|---|---|
用户输入响应延迟 | 200-300ms | 50-100ms | 4x |
渲染中断次数 | 0(无中断) | 3-5次/秒 | N/A |
无用渲染比例 | 60-70% | 10-20% | 80%↓ |
内存占用 | 高 | 低(位运算) | 60%↓ |
实际应用建议
-
优先级指定:
// 使用 startTransition 标记低优先级更新 import { startTransition } from 'react'; function loadData() { startTransition(() => { setReviews(data); // 低优先级更新 }); }
-
性能优化:
// 使用 useDeferredValue 延迟非关键内容 const deferredReviews = useDeferredValue(reviews, { timeoutMs: 2000 // 延迟2秒 });
-
调试工具:
// 启用 React DevTools 的 Lane 调试 __REACT_DEVTOOLS_GLOBAL_HOOK__.showLanes = true;
Lanes 机制使 React 能够像交通管理系统一样,高效调度不同类型的"更新车辆",确保:
-
紧急更新(救护车)优先通行
-
普通更新(小汽车)批量通行
-
低优先级更新(货车)在空闲时通行
这种精细的优先级控制是现代复杂应用保持流畅的关键所在。