深度掌握 ReactJS 高级概念:前端开发者必备

开发 前端
ReactJS 的核心运行机制就是把 JSX 转成 ​​React.createElement()​​ 调用,再把这些 “React Element” 组成虚拟 DOM。

这篇文章汇总了 ReactJS 中值得深入研究的高级概念。读完后,不仅在前端面试中能更胸有成竹,还能自行开发一个类似 ReactJS 的 UI 库。

目录

  1. Rendering 的含义与过程
  2. Re-rendering 发生的机制及原因
  3. Virtual DOM 的原理
  4. Reconciliation 算法的运行方式
  5. ReactJS 的性能优化方案

1. 什么是 Rendering?它是如何进行的?

在 React 中,我们常提到 “渲染(Rendering)”。本质上,它是把 JSX 或通过 React.createElement() 生成的元素转换为实际的 DOM 节点,让页面在浏览器中展现出来。

JSX 与 React.createElement()

JSX(JavaScript XML)是一种 React 引入的语法糖。浏览器只能理解 JavaScript,所以 JSX 需要先经过 Babel 编译成 React.createElement() 的调用,才会生成所谓的 “React Element”(一个纯粹的 JavaScript 对象)。

示例:

例 1

// JSX 写法
const jsx = <h1>Hello, React!</h1>;

// Babel 转换后
const element = React.createElement("h1", null, "Hello, React!");

image.png

例 2

const Jsx = <h1 className="title">Hello, React!</h1>;

// Babel 转换后
const element = React.createElement("h1", { className: "title" }, "Hello, React!");

例 3

<div>
  <h1>Hello</h1>
  <p>Welcome to React</p>
</div>

// Babel 转换后

const element = React.createElement(
  "div",
  null,
  React.createElement("h1", null, "Hello"),
  React.createElement("p", null, "Welcome to React")
);

例 4

const Jsx = <Card data = {cardData} />

// Babel 转换后
const element = React.createElement(Card, { data: cardData })

React.createElement(type, props, ...children) 会返回一个描述 DOM 结构的 JS 对象,如:

{
  type: "h1",
  props: {
    className: "title",
    children: "Hello, React!"
  },
  key: null,
  ref: null,
  ...
}

React 最终会根据这些对象来构造真实 DOM。

初次渲染(Initial Rendering)

初次渲染的流程大致是:

  1. React 组件(函数式/类)返回 JSX
  2. Babel 将其转换为 React Element
  3. React 构建出一份虚拟的 DOM 结构(Virtual DOM)
  4. React 将虚拟 DOM 与真实 DOM 同步,页面上出现相应的节点

大型应用通常有成百上千个组件嵌套,最终 React 会构建出巨大的虚拟 DOM 树,再将其 “映射” 到真实 DOM。初次加载时生成的真实 DOM 较多,耗时也更多。

2. 什么是 Re-rendering,组件何时会重新渲染?

Re-rendering 指组件为了更新 UI,会再次执行渲染过程。React 只在需要时重新渲染,而不是盲目全量刷新,以提高效率。

触发重新渲染的场景

  • State 变化
    当 useState 或 this.setState 更新了 state,组件会重新渲染。
import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  console.log("Counter Re-Rendered!");

  return (
    <div>
      <h1>Count: {count}</h1>
      <button notallow={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default Counter;
  • Props 改变
    如果父组件传递的新 props 和旧 props 不同,子组件会重新渲染。
function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <Child count={count} />
      <button notallow={() => setCount(count + 1)}>Update Count</button>
    </div>
  );
}


function Child({ count }) {
  console.log("Child Re-Rendered!");

  return <h1>Count: {count}</h1>;
}

export default Parent;
  • 父组件重渲染
    只要父组件重新渲染,即使子组件的 props 没变,子组件也默认跟着渲染。
function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <Child />
      <button onClick={() => setCount(count + 1)}>Re-Render Parent</button>
    </div>
  );
}

function Child() {
  console.log("Child Re-Rendered!");
  return <h1>Hello</h1>;
}

点按钮后,父组件因为 state 改变而重渲染,Child 也跟着渲染。如果不想子组件重复渲染,可以使用 React.memo(Child),阻止不必要的更新。

React 18+ 中的严格模式双重渲染

在开发模式下,<React.StrictMode> 会让组件在初始化时执行两次渲染,以检测副作用。这在生产环境不会触发,只需要知道这是为了帮助开发调试即可。

import React from "react";
import ReactDOM from "react-dom";

function App() {
  console.log("Component Rendered!");
  return <h1>Hello</h1>;
}

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

3. 理解 Virtual DOM

虚拟 DOM(V-DOM)是 React 在内存中维护的一份轻量级 DOM 结构,能显著减少对真实 DOM 的频繁操作。

  • 真实 DOM 操作昂贵
  • 虚拟 DOM 先在内存中对比,再只更新有差异的地方

工作流程

  • 生成初始虚拟 DOM
  • 数据或 props 变动时,生成新的虚拟 DOM
  • 对比新旧虚拟 DOM 的差别(Diff 过程)
  • 有变化的地方才更新真实 DOM

这种按需更新机制提升了性能。比方说文本从 “Count: 0” 变成 “Count: 1”,React 只会修改文本内容,而不会重新创建整个 <h1> 标签。

4. Reconciliation:React 的高效更新算法

Reconciliation 是 React 用来高效处理 DOM 更新的过程,核心是 Diff 算法。

Diff 规则

  • 不同类型的元素
    如果 type 变了(比如从 <h1> 变 <p>,或从 Card 组件变成 List 组件),React 会销毁原节点并新建节点。
function App({ showText }) {
  return showText ? <h1>Hello</h1> : <p>Hello</p>;
}
  • 相同类型的元素
    如果 type 相同,只更新变更部分。例如修改属性或文本内容。
function App({ text }) {
  return <h1 className="title">{text}</h1>;
}

将 text 从 "Hello" 改为 "World" 会使 React 仅更新文本。

  • 列表中的 Key
    当使用 map() 渲染列表时,务必给每个项加唯一 key,这样 React 才能跟踪列表项,做最小化更新。如果没有 key(或 key 不唯一),React 很可能重渲染整个列表,导致性能浪费。

代码错误(无key) → diff 效率低

function List({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li>{item}</li>
      ))}
    </ul>
  );
}

如果在开始时添加了一个新项目,React 会重新渲染所有 <li> 元素,这样做很慢,因为 React 无法跟踪没有键的单个项目。

良好代码(key) → 优化对账

function List({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

5. ReactJS 的性能优化技巧

5.1 React.memo():防止不必要的子组件重复渲染

在父组件刷新而子组件 props 未变的情况下,React.memo(Child) 能阻止子组件重复渲染。

const ChildComponent = React.memo(({ count }) => {
  console.log("Child render");
  return <h2>Count: {count}</h2>;
});

只要 count 没变化,就不会重复渲染。

5.2 useMemo():缓存昂贵计算结果

如果某个函数计算量大且多次使用相同参数,可以用 useMemo 缓存结果,避免重复计算。

function expensiveComputation(num) {
  console.log("Computing...");
  return num * 2; 
}

function App() {
  const [number, setNumber] = useState(5);
  const memoizedValue = useMemo(() => expensiveComputation(number), [number]);

  // 每次渲染,只要 number 不变,就不会重复执行 expensiveComputation
  return <h2>Computed Value: {memoizedValue}</h2>;
}

5.3 useCallback():缓存函数引用,减少子组件不必要的渲染

React 每次渲染都会重新创建函数。如果子组件接收函数作为 props,默认会认为 props 变了,进而触发子组件渲染。用 useCallback() 可以让函数在依赖不变时保持相同引用。

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []);

  return (
    <div>
      <ChildComponent onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

这样 ChildComponent 不会因为 onClick prop 每次都换新函数而被动重渲染。

总结

ReactJS 的核心运行机制就是把 JSX 转成 React.createElement() 调用,再把这些 “React Element” 组成虚拟 DOM。通过比较新旧虚拟 DOM 的差异(Reconciliation),React 能用最小代价更新真实 DOM。基于这个原理,就能延伸出许多优化策略,比如:

  • 使用 React.memo 防止子组件反复刷新
  • 通过 useMemouseCallback 缓存耗时操作及函数引用
  • 在列表中使用 key,避免不必要的遍历和重绘

这些技巧能够在大规模项目中让性能和可维护性都大幅提升,也是真正掌握 ReactJS 的关键所在。

责任编辑:武晓燕 来源: 大迁世界
相关推荐

2021-12-15 20:06:48

ReactJSSentry开发者

2019-03-12 10:38:18

前端开发Nginx

2021-04-08 10:40:24

前端工具代码

2013-08-08 10:26:08

前端开发Web

2013-10-08 10:42:27

前端开发开发

2021-04-01 07:52:57

前端开发技术热点

2025-01-23 13:58:17

2024-08-09 15:01:00

2023-11-30 15:30:19

Python编程语言

2012-02-13 10:21:11

Skala PreviiOS应用

2017-01-16 13:15:19

前端开发者清单

2014-07-08 10:30:59

开发者开发语言

2022-04-27 09:48:56

JS前端开发

2013-07-19 09:47:57

White ElephHadoopLinkedIn

2014-04-18 13:20:34

Android安卓开发工具

2011-03-01 13:10:06

WebjQueryHTML 5

2017-10-23 09:27:47

2013-12-30 13:46:27

Android开发者

2022-09-15 17:08:20

JavaScripWeb开发

2011-01-11 11:35:17

jQueryAndroidgoogle
点赞
收藏

51CTO技术栈公众号