”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 反应虚拟 DOM

反应虚拟 DOM

发布于2024-11-08
浏览:705

Introduction

Hi, Gleb Kotovsky is here!

Today I wanna talk about Virtual DOM, specifically - React Virtual DOM

So, the virtual DOM (Virtual Document Object Model) is a cool programming idea that keeps a "virtual" version of a user interface in memory. This version syncs up with the browser's DOM (Document Object Model) using a library.

You’ll find the virtual DOM is a big part of many JavaScript front-end frameworks, and it’s one of the reasons they’re so efficient. In this article, we're going to dive into how the virtual DOM works in React and why it’s important for the library.

What is the DOM?

When a webpage loads in a browser, it typically receives an HTML document from the server. The browser then builds a logical, tree-like structure from this HTML to render the requested page for the user. This structure is known as the DOM.

The Document Object Model (DOM) represents a logical tree that describes a document. Each branch of the tree ends in a node , which contains an object . Because the browser parses the document into this tree structure, there is a need for methods that allow for programmatic access to the tree, enabling modifications to the document's structure, style, or content. This necessity led to the development of the DOM API, which offers these methods for manipulating the nodes representing the elements in the tree.

React Virtual DOM

React's Virtual DOM Implementation

To optimize re-rendering in websites and applications, many JavaScript frameworks offer different strategies. However, React employs the concept of the virtual DOM.

The virtual DOM in React represents the user interface as a "virtual" tree structure, where each element is a node containing an object. This representation is maintained in memory and synchronized with the browser's DOM through React's React DOM library.

When React and many other famous frameworks uses Virtual DOM, Svelte meanwhile has no Virtual DOM. Svelte works directly with the DOM in the browser and modifies it as needed.

Here's a simple example to illustrate the Virtual DOM in a React component:

import React, { useState } from 'react';

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

  const increment = () => setCount(count   1);

  return (
    

Count: {count}

); }

In this example:

  • The component renders a counter and a button.
  • When the button is clicked, the state is updated, prompting React to create a new Virtual DOM tree.
  • The diffing algorithm checks what has changed (only the count) and updates the real DOM accordingly.

After the component is first rendered and the state is count: 0, the actual DOM will look like this:

Counter

Count: 0

How the Virtual DOM Works:

Here's a simple example to illustrate the Virtual DOM in a React component, starting with the component definition:

1. Component Definition

import React, { useState } from 'react';

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

  const increment = () => setCount(count   1);

  return (
    

Counter

Count: {count}

); }

2. Initial Render Process

2.1 Component Initialization

When the component is first rendered, React calls the Counter function.

2.2 State Initialization

useState(0) initializes the component's state to 0.

2.3 Creating the Virtual DOM

React generates a Virtual DOM tree using the component's returned JSX structure. This tree is a lightweight representation of the UI.

For the initial render, the Virtual DOM might look like this:

{
  "type": "div",
  "props": {
    "children": [
      { "type": "h1", "props": { "children": "Counter" } },
      { "type": "p", "props": { "children": "Count: 0" } },
      { "type": "button", "props": { "children": "Increment" } }
    ]
  }
}

2.4 Updating the Real DOM

React then takes this Virtual DOM and calculates what changes need to be made to the actual DOM. In this case, it creates the following HTML:

Counter

Count: 0

3. User Interaction

When a user clicks the "Increment" button, the following steps occur:

3.1 Event Handling

The button's onClick event triggers the increment function, calling setCount(count 1).

3.2 State Update

The component's state is updated, which causes React to know that it needs to re-render the component with the new state.

4. Re-render Process

4.1 Component Re-invocation

React calls the Counter function again due to the state change.

4.2 New Virtual DOM Creation

A new Virtual DOM tree is created reflecting the updated state:

{
  "type": "div",
  "props": {
    "children": [
      { "type": "h1", "props": { "children": "Counter" } },
      { "type": "p", "props": { "children": "Count: 1" } },
      { "type": "button", "props": { "children": "Increment" } }
    ]
  }
}

4.3 Diffing the Virtual DOM

React compares the new Virtual DOM with the previous Virtual DOM. It identifies what has changed—in this case, the text in the

tag has changed from "Count: 0" to "Count: 1".

4.4 Reconciliation

Only the parts of the real DOM that have changed are updated. In this case, React updates the real DOM to reflect the new count:

Counter

Count: 1

5. Performance Optimization

5.1 Batching Updates

If multiple state updates occur in rapid succession (e.g., multiple button clicks), React may batch these updates together for efficiency, minimizing the number of re-renders and DOM updates.

Common Problems with React Virtual DOM and How to Avoid Them

  1. Performance Bottlenecks
    • Issue: Excessive re-renders can occur even with the Virtual DOM.
    • Solution: Use React.memo to memoize functional components.
const MyComponent = React.memo(({ value }) => {
  console.log('Rendered: ', value);
  return 
{value}
; });

Legacy: Use shouldComponentUpdate in class components:

class MyClassComponent extends React.Component {
  shouldComponentUpdate(nextProps) {
    return nextProps.value !== this.props.value;
  }

  render() {
    return 
{this.props.value}
; } }
  1. Inefficient Key Management
    • Issue: Improper handling of keys in lists can lead to bugs.
    • Solution: Use unique and stable keys, not array indices.
   const items = ['Apple', 'Banana', 'Cherry'];
      return (
        
    {items.map(item => (
  • {item}
  • // Prefer unique values as keys ))}
);
  1. Overusing State and Updates
    • Issue: Too many state updates lead to performance issues.
    • Solution: Combine related states
const [state, setState] = useState({
  name: '',
  age: 0,
});

const updateAge = (newAge) => {
  setState(prevState => ({ ...prevState, age: newAge }));
};

  1. Using Inline Functions
    • Issue: Inline functions create new instances on every render.
    • Solution: Use useCallback to memoize functions.
const increment = useCallback(() => {
  setCount(c => c   1);
}, []); // Only recreate the function if dependencies change

  1. Deep Component Trees
    • Issue: Deeply nested components trigger multiple re-renders.
    • Solution: Use context.
const CountContext = React.createContext();

const ParentComponent = () => {
  const [count, setCount] = useState(0);

  return (
    
      
    
  );
};

const ChildComponent = () => {
  const { count, setCount } = useContext(CountContext);
  return 
setCount(count 1)}>Count: {count}
; };
  1. Excessive Re-renders Due to Parent Component Updates
    • Issue: Child components re-render when parents update.
    • Solution: Memoize child components.
const ChildComponent = React.memo(({ count }) => {
  return 
Count: {count}
; });
  1. Inefficient Rendering of Expensive Components
    • Issue: Expensive components can slow down the app.
    • Solution: Use React.lazy and React.Suspense.
const LazyComponent = React.lazy(() => import('./LazyComponent'));

const App = () => (
  Loading...}>
    
  
);

  1. Managing Side Effects
    • Issue: Side effects can cause bugs if not managed properly.
    • Solution: Use useEffect with proper dependencies.
useEffect(() => {
  const timer = setTimeout(() => {
    console.log('Time elapsed');
  }, 1000);

  return () => clearTimeout(timer); // Cleanup on unmount or if dependencies change
}, [dependencies]); // Replace with actual dependency

  1. Confusion Between State and Props
    • Issue: Misunderstanding when to use state vs. props.
    • Solution: Use props for externally managed data and state for local data.
const ParentComponent = () => {
  const [name, setName] = useState('John');

  return ;
};

const ChildComponent = ({ name, setName }) => (
  

{name}

);
  1. Neglecting Accessibility
    • Issue: Accessibility concerns can be ignored.
    • Solution: Use semantic HTML and accessibility tools.
const AccessibleButton = () => (
  
);

Conclusion

To wrap things up, React’s Virtual DOM is a fantastic feature that really boosts the performance of your web applications. By creating a lightweight version of the actual DOM, React can make updates more efficiently, avoiding the slowdowns that come with direct DOM manipulation.

That said, it’s important to watch out for common issues like excessive re-renders, poor key management in lists, and mixing up state and props. By keeping some best practices in mind—like using memoization, deploying context for handling state, and managing side effects wisely—you can get the most out of React and keep your apps running smoothly.

Happy hacking!

Resources

1) https://www.geeksforgeeks.org/reactjs-virtual-dom/
2) https://svelte.dev/blog/virtual-dom-is-pure-overhead
3) https://refine.dev/blog/react-virtual-dom/#introduction

版本声明 本文转载于:https://dev.to/gaundergod/react-virtual-dom-45al?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-12-28
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-12-28
  • 大批
    大批
    方法是可以在对象上调用的 fns 数组是对象,因此它们在 JS 中也有方法。 slice(begin):将数组的一部分提取到新数组中,而不改变原始数组。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index p...
    编程 发布于2024-12-28
  • 除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有哪些地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为的主要场景bool:语句:if、whi...
    编程 发布于2024-12-28
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    编程 发布于2024-12-28
  • HTML 格式标签
    HTML 格式标签
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    编程 发布于2024-12-28
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1 和 $array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求...
    编程 发布于2024-12-28
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-12-28
  • 插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入数据时如何修复“常规错误:2006 MySQL 服务器已消失”?
    插入记录时如何解决“一般错误:2006 MySQL 服务器已消失”介绍:将数据插入 MySQL 数据库有时会导致错误“一般错误:2006 MySQL 服务器已消失”。当与服务器的连接丢失时会出现此错误,通常是由于 MySQL 配置中的两个变量之一所致。解决方案:解决此错误的关键是调整wait_tim...
    编程 发布于2024-12-28
  • 尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    解决 PHP 中的 POST 请求故障在提供的代码片段中:action=''而不是:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"检查 $_POST数组:表单提交后使用 var_dump 检查 $_POST 数...
    编程 发布于2024-12-28
  • 如何准确地透视具有不同记录的数据以避免丢失信息?
    如何准确地透视具有不同记录的数据以避免丢失信息?
    有效地透视不同记录透视查询在将数据转换为表格格式、实现轻松数据分析方面发挥着至关重要的作用。但是,在处理不同记录时,数据透视查询的默认行为可能会出现问题。问题:忽略不同值考虑下表:------------------------------------------------------ | Id ...
    编程 发布于2024-12-27
  • 为什么 C 和 C++ 忽略函数签名中的数组长度?
    为什么 C 和 C++ 忽略函数签名中的数组长度?
    将数组传递给 C 和 C 中的函数 问题:为什么 C 和C 编译器允许在函数签名中声明数组长度,例如 int dis(char a[1])(当它们不允许时)强制执行?答案:C 和 C 中用于将数组传递给函数的语法是历史上的奇怪现象,它允许将指针传递给第一个元素详细说明:在 C 和 C 中,数组不是通...
    编程 发布于2024-12-26
  • 如何删除 MySQL 中的重音符号以改进自动完成搜索?
    如何删除 MySQL 中的重音符号以改进自动完成搜索?
    在 MySQL 中删除重音符号以实现高效的自动完成搜索管理大型地名数据库时,确保准确和高效至关重要数据检索。使用自动完成功能时,地名中的重音可能会带来挑战。为了解决这个问题,一个自然的问题出现了:如何在 MySQL 中删除重音符号以改进自动完成功能?解决方案在于为数据库列使用适当的排序规则设置。通过...
    编程 发布于2024-12-26
  • 如何在MySQL中实现复合外键?
    如何在MySQL中实现复合外键?
    在 SQL 中实现复合外键一种常见的数据库设计涉及使用复合键在表之间建立关系。复合键是多个列的组合,唯一标识表中的记录。在这个场景中,你有两个表,tutorial和group,你需要将tutorial中的复合唯一键链接到group中的字段。根据MySQL文档,MySQL支持外键映射到复合键。但是,要...
    编程 发布于2024-12-26
  • 为什么我的 JComponent 隐藏在 Java 的背景图像后面?
    为什么我的 JComponent 隐藏在 Java 的背景图像后面?
    调试背景图像隐藏的 JComponent在 Java 应用程序中使用 JComponent(例如 JLabels)时,必须确保正确的行为和可见度。如果遇到组件隐藏在背景图像后面的问题,请考虑以下方法:1。正确设置组件透明度:确保背景面板是透明的,以允许底层组件透过。使用setOpaque(false...
    编程 发布于2024-12-26

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3