深入理解 React JSX、虚拟DOM

235 阅读3分钟

目标

本文的目标:理解 JSX、虚拟DOM,以及 vdom 的优点和缺点,理解这些有助于对项目的优化和对虚拟DOM的理解。

JSX 是什么

JSX 权威解释 JSX 是一种 JavaScript 的语法扩展,它允许我们在 JavaScript 代码中直接书写类似于 HTML 的标记语言,从而使得编写 React 组件更加方便和直观。

既然是语法扩展,那么 JS 解析器肯定是不认识的,就需要 babel 将其转换为 JS 解析器能理解的 JS 语法代码。 转换由 babel 相关插件附理,下面是转换前后的对比:

// ./src/main.jsx

const el = (
  <div>
    hello, <p key="111" style={{ color: 'red' }}>GWJ</p>
    {Math.random() < 0.5 && <main>Content</main>}
  </div>
);

转换为JS语法:

const el = const el = jsx('div', {
  children: [
    'hello',
    
    jsx('p', {
      style: {
        color: 'red',
      },
      children: 'GaoWuJie',
    }),
    
    Math.random() < 0.5 &&
      jsx('main', {
        children: 'Content',
      }),
  ],
});

VDOM

jsx 函数最终在运行时生成的对象,就称作为 VDOM。虚拟DOM用最小的内存用来描述真实的DOM,即用最小的内存用来描述 UI 界面。下面就是 JSX 在运行时生成的 vdom(虚拟对象树):

const el = {
  type: 'div',
  props: {
    children: [
      'hello',
      
      {
        type: 'p',
        props: {
          style: { color: 'red' },
          children: 'GWJ'
        }
      },
      
      {
        type: 'main',
        props: {
          children: 'Content'
        }
      }
    ]
  }
}

优点

  1. 提高一般性更新时性能:VDOM 可以避免不必要的 DOM 操作,因为它可以比较两次VDOM之间的差异,只更新需要更新的部分。这种方式比直接操作 DOM 更快,因为直接操作 DOM 会导致浏览器重绘和重排,而VDOM 可以减少这些操作,因为按需更新。

例外:

  1. 当 VDOM tree 非常大时,VDOM diff 操作是很耗时的,而 VDOM diff 又是同步的,这时 VDOM 反而是累赘;所以,我们尽量要组件细分化,频繁更新尽可局部性。
  2. 当 VDOM diff 后发现更新的 dom 有很多时,VDOM diff 反而也是累赘了。

总结:当更新量小时,vdom diff 才有助于性能的提升,所以我们尽量要组件细分化,频繁更新尽可能局部性。

  1. 提高开发效率:VDOM 可以让开发者更方便地进行组件化开发,因为它可以将整个组件树看作一个单一的整体,而不是分散的部分。这使得开发者可以更快地编写和维护代码。

  2. 跨平台支持:VDOM 不依赖于浏览器的实现,因为就是 VDOM 就是 JS 对象。因此可以在多个平台上使用,例如 Node.js、React Native 等。

缺点

  1. 初次生成造成性能浪费:初次渲染时,由于还没有前一次的 VDOM tree,React 需要先生成完整的 VDOM tree,然后再将其转换为真实的 DOM tree,这个过程比直接操作 DOM 更加耗时。

注意: 当项目很庞大时,VDOM tree 很大,VDOM 在初次渲染时可能会带来一些额外的开销,这个开销可能无法被忽略。在这种情况下,我们可以采取一些优化措施来提高初次渲染的性能,比如使用代码拆分懒加载等技术来减少页面的初始渲染量

另外,需要注意的是,虚拟 DOM 可以帮助我们避免一些常见的性能问题,比如频繁的 DOM 操作、重复的元素计算等。在项目很庞大时,这些问题可能会更加突出,因此虚拟 DOM 对性能的优化作用可能也更加明显。 总之,在实际开发中,我们需要根据具体情况来选择合适的技术和优化策略,以提高应用的性能和用户体验。

  1. 需要额外的内存和计算开销:虚拟 DOM 需要在内存中维护一个虚拟的 DOM 树,因此会占用一定的内存空间。而且每次渲染都需要计算虚拟 DOM 和真实 DOM 之间的差异,这也会占用一定的计算资源,这也是无法避免的,因为要按需更新 DOM 不不得对前后 VDOM 进行计算,更新需要更新的 DOM。

  2. 不支持直接操作 DOM:VDOM 不能直接操作 DOM,因此在一些需要直接操作 DOM 的场景下可能不太方便。比如实现一些高级动画效果可能需要直接操作 DOM。

  3. 可能存在重复渲染的问题:因为 VDOM 是在内存中维护的,所以在某些情况下它可能会不准确,导致组件重复渲染。这个问题可以通过使用 React.memo 或 PureComponent 进行优化来避免。

JSX 细节

一些历史

React 17 以前,JSX语法babel 编译,会引入下面的代码,这需要我们手动引入 React

import React from 'react'; 
React.createElement ...

17 以及之后,无需我们手动引入 jsx api,babel 编译时,会自动引入如下代码:

import { jsxDEV } from 'react/jsx-runtime';
jsxDEV(......);

当然,jsx 函数和 React.createElement 是一样的,都是生成 ReactElement,即vdom