React 彻底改变了我们构建用户界面的方式,但您是否想过,在编写 React 代码时,幕后究竟发生了什么? 本指南将带您了解 React 内部工作原理的每个步骤,从编写 JSX 到在浏览器中查看最终结果。 在深入探讨 React 之前,我们先来快速了解一下 JavaScript 的内部工作原理。
第 1 章:JavaScript 内部工作原理 - 快速概览 在深入研究 React 之前,了解 JavaScript 背后的工作原理非常重要。
当你在浏览器中运行 JavaScript 时,它由 JavaScript 引擎(例如 Chrome 的 V8)执行。引擎首先将代码解析为抽象语法树 (AST),然后使用即时 (JIT) 编译器将其转换为优化的机器码,以实现高效执行。 JavaScript 是单线程的,通过调用栈一次执行一个任务。API 调用、计时器或 DOM 事件等异步操作使用 Web API 和事件循环进行处理,允许任务在后台运行而不会阻塞主线程。 每个函数在其自己的执行上下文中运行,该上下文跟踪变量和作用域。这些上下文形成一个作用域链,从而实现闭包以及跨嵌套函数对变量的一致访问。 第 2 章:编写 React 组件 React 应用程序使用组件构建而成。组件是一些独立且可复用的代码片段,用于描述用户界面的外观。您可以将组件想象成乐高积木:每个组件都有特定的用途,并且可以与其他组件组合以创建复杂的结构。
function Welcome(props) { const [count, setCount] = useState(0);
return (
Hello, {props.name}!
<button onClick={() => setCount(count + 1)}> Clicked {count} times现在让我们了解一下组件的生命周期: 每个 React 组件从创建到从 DOM 中移除,都会经历一系列阶段。这些阶段统称为组件生命周期,它们允许你在特定时间点运行代码,例如获取数据或清理资源。
1.Class 组件生命周期
挂载(创建):当组件首次添加到 DOM 时。constructor () → 初始化状态和绑定方法 render() → 返回 JSX componentDidMount() → 在组件添加后运行
更新:当状态或属性发生变化时,触发重新渲染。render () → 创建更新的 UI componentDidUpdate() → 更新后运行
卸载:当组件从 DOM 中移除时。componentWillUnmount () → 清理任务,例如移除监听器
- 功能组件生命周期(带 Hooks)
安装:第一次渲染后运行。 useEffect(() => { … }, []) 更新:每当依赖项发生变化时运行。 useEffect(() => { … }, [dependencies]) 卸载:在删除组件之前运行清理代码。 useEffect(() => { return () => { … } }, []) 第 3 章:JSX - JavaScript 和 HTML 之间的桥梁 JSX(JavaScript XML)是一种语法扩展,允许你直接在 JavaScript 中编写类似 HTML 的代码。虽然 JSX 看起来像 HTML,但它实际上是一种语法糖,可以转换为常规的 JavaScript 函数调用。
// This JSX code... const element =
Hello, World!
;// ...becomes this JavaScript: const element = React.createElement( 'h1', { className: 'greeting' }, 'Hello, World!' ); React.createElement() 函数是 React 元素创建系统的核心。它接受三个参数:
类型:元素类型(HTML 元素的字符串,组件的函数) Props:包含属性和事件处理程序的对象 子项:子元素或文本内容 第 4 章:Babel 的实际功能 Babel 是一个 JavaScript 转译器,主要处理语法转换,例如将 JSX 或现代 ES6+ JavaScript 转换为浏览器可以理解的代码。其核心任务是:
解析:将代码转换为抽象语法树(AST) 转换:将 JSX 元素转换为 React.createElement() 调用 代码生成:输出与浏览器兼容的 JavaScript Polyfill 与旧版浏览器相关,但又相互独立。它们是一些代码片段,用于添加旧版浏览器中缺失的 JavaScript 功能(例如 Promise、Array.from、Object.assign)。Babel 可以通过“ @babel /preset-env”或 core-js 等工具注入 Polyfill,但 Polyfill 并非 Babel 的主要功能;它们是可选的,通常需要明确配置。
// Before Babel (what you write): function App() { return (
My App
Click me!// After Babel (what the browser receives): function App() { return React.createElement( 'div', null, React.createElement('h1', null, 'My App'), React.createElement(Button, { onClick: handleClick }, 'Click me!') ); } 第五章:Webpack – 模块打包器 在 Babel 转换代码之后,您仍然需要一种方法将所有 JavaScript、CSS 和图像文件打包成浏览器可以高效加载的内容。这就是 Webpack 的作用所在。
Webpack 的作用:
捆绑模块:将所有 JS 文件和依赖项合并到一个(或几个)捆绑文件中。 处理资产:可以使用加载器处理 CSS、图像、字体等。 优化代码:支持最小化、树形优化和代码拆分,使您的应用程序更快。 与插件一起使用:添加额外的功能,如 HTML 生成、环境变量和缓存。 简而言之,Babel 转换您的代码,然后 Webpack 将其打包在一起,以便浏览器可以理解并有效地运行您的应用程序。
第六章:虚拟 DOM - React 的秘密武器 虚拟 DOM 是 React 最具创新性的功能,它是真实 DOM 的轻量级内存表示。React 不会直接操作浏览器的 DOM(这很慢),而是在 JavaScript 内存中创建并维护一个虚拟副本。
虚拟 DOM 过程涉及几个步骤:
初始渲染:React 创建一个完整的虚拟 DOM 树来代表整个 UI 状态变化:当数据发生变化时,React 会创建一个新的虚拟 DOM 树 差异:React 将新树与前一棵树进行比较 协调:仅将差异应用于真实 DOM 协调算法 React 的协调算法使虚拟 DOM 更加高效。它使用复杂的 diffing 过程来最小化 DOM 更新:
元素类型比较:如果两个元素具有不同的类型(例如 vs ),React 会拆除旧树并从头开始构建一棵新树。 相同类型元素:对于相同类型的元素,React 保持相同的 DOM 节点,并且只更新更改的属性。 列表协调:React 使用 key prop 来识别列表中哪些项目已更改、已添加或已删除,从而使更新更加高效。 第七章:React Fiber - 现代引擎 React Fiber 是 React 核心算法的完全重写,于 React 16 中引入。它通过启用增量渲染解决了原始“堆栈协调器”的局限性。
Fiber 的必要性 最初的 React 协调器存在一个问题:它是同步工作的,这意味着一旦它开始更新 UI,就无法被中断。对于复杂的应用程序来说,这可能会导致浏览器卡住,使应用程序感觉无响应。Fiber 通过以下方式解决了这个问题:
将工作分解成单元:每个组件更新都成为一个可以暂停和恢复的工作单元 优先更新:用户交互比后台更新具有更高的优先级 启用并发功能:可以同时进行多个更新 Fiber 架构 应用程序中的每个组件都对应一个 Fiber 节点 - 一个包含以下内容的 JavaScript 对象:
类型:组件类型 Props:组件的属性 状态:组件的内部状态 子/兄弟指针:指向其他 Fiber 节点的链接 这些 Fiber 节点形成一个 Fiber 树,反映了您的组件层次结构,使 React 能够有效地遍历和更新组件。
第 8 章:状态管理和数据流 React 遵循单向数据流。 数据通过 props 从父组件向下流向子组件, 而更改则通过回调函数向上流动。State 与 Prop
状态:
组件内部 可以由组件本身修改 更改触发重新渲染 使用 useState(功能)或 setState(类)进行管理 道具:
从父组件传递的外部数据 在接收组件内只读 实现组件可重用性 可以包括儿童 更改来自父组件 第 9 章:完整的 React 渲染过程 让我们把所有内容放在一起,看看运行 React 应用程序时会发生什么。
步骤 1:构建过程
您可以使用 JSX 和现代 JavaScript 编写 React 组件。 Babel 将您的代码转换为 React.createElement() 调用。 Webpack(或其他捆绑器)为浏览器打包所有文件。 步骤2:初始渲染
React 创建代表您的 UI 的初始虚拟 DOM 树。 协调器将虚拟 DOM 转换为真实的 DOM 元素。 浏览器显示您的应用程序。 步骤3:用户交互和更新
当用户与您的应用进行交互(点击、输入等)时, 状态更新通过 setState 或 hook 更新程序触发。 React 使用 Fiber 的调度程序安排重新渲染。 创建一个新的虚拟 DOM 树。 差异算法比较新旧虚拟 DOM 树。 协调器仅对真实 DOM 应用必要的最小更改。 浏览器仅更新 UI 中发生变化的部分。 步骤 4:光纤工作环
React Fiber 将工作分解为可以暂停和恢复的单元。 首先处理高优先级更新(如用户输入)。 不太紧急的工作会暂停并稍后完成,以保持应用程序的响应。 简而言之,React 转换您的代码→构建虚拟表示→使用协调和 Fiber 高效更新浏览器→保持您的 UI 快速且响应迅速。
第 10 章:性能优化 React 包含几个内置优化功能,可让您的应用程序运行更快:
批量更新 React 会自动批量处理同一事件处理程序中发生的多个状态更新,从而减少重新渲染的次数。 组件记忆化 React 可以使用 React.memo() 和 useMemo() 等技术,在组件的 props 没有改变时跳过重新渲染组件。 可以使用 React.lazy() 和 Suspense 按需加载延迟加载组件,从而减少初始包的大小。 总结 现在,我们已经完成了从编写 JSX 到在浏览器中查看应用的整个过程,您应该对幕后发生的事情有了清晰的了解。React 不仅仅是魔法,它是一个精心设计的系统,它使用 Babel 转换代码,使用 Webpack 打包代码,使用 Virtual DOM 和 Fiber 保持快速更新,并使用 state 和 props 管理数据流。
理解这一点会给你带来巨大的优势。你可以编写更简洁的代码,更智能地调试,甚至像专业人士一样优化你的应用。
所以,下次你点击“保存”并看到 UI 更新时,请花点时间欣赏一下后台发生的一系列流程。你对这个内部流程了解得越多,你作为一名 React 开发者就会越强大、越自信。
如果您觉得这篇文章有帮助,别忘了点赞、评论和关注。我会发布更多博客文章,以通俗易懂的方式深入解析 JavaScript、React 和前端内部原理。让我们一起继续学习,编码愉快!作者www.lglngy.com