虚拟DOM的设计目的是提供一种更高效的方式来构建和更新Web应用程序的用户界面,同时降低了性能开销和提高了开发效率。有时候我们会遇到这个问题,虚拟 dom 性能一定会超过原生 dom?
答案是:不一定。
一、页面初始化渲染
像 React/Vue 这种基于虚拟 dom 的框架,在第一渲染页面的时候,如果页面内容较多,需要虚拟 dom 的运行时计算出实际 dom, 此时还没有涉及到重新渲染复用虚拟 dom, 此时与运行计算时间肯会造成页面的白屏时间。
二、静态内容渲染
- React 渲染
ts
// 引入React
import React from 'react';
import ReactDOM from 'react-dom';
// 一个简单的React组件
const SimplePage = () => (
<div>
<h1>Hello, World!</h1>
<p>This is a simple static page.</p>
</div>
);
// 渲染React组件到实际DOM
ReactDOM.render(<SimplePage />, document.getElementById('app'));
- 原生 dom 渲染
ts
// 获取实际DOM容器
const container = document.getElementById('app');
// 创建和设置实际DOM元素
const div = document.createElement('div');
const heading = document.createElement('h1');
heading.textContent = 'Hello, World!';
const paragraph = document.createElement('p');
paragraph.textContent = 'This is a simple static page.';
// 将元素添加到容器
div.appendChild(heading);
div.appendChild(paragraph);
container.appendChild(div);
对于页面(组件)中包含了静态内容的时候,使用 React 渲染,需要 React 的运行时计算过程,显然没有原生 dom 直接渲染的性能好。
三、大规模数据更新
tsx
const container = document.getElementById('container');
function updateData() {
const data = Array.from({ length: 1000 }, (_, index) => index);
const virtualList = data.map((item) => {
return <li key={item}>{item}</li>;
});
ReactDOM.render(<ul>{virtualList}</ul>, container);
}
updateData();
setInterval(() => {
updateData(); // 模拟每秒更新一次数据
}, 1000);
一个每秒更新一次数据。尽管虚拟DOM通常能够提高性能,但由于每秒更新大量数据,虚拟DOM需要不断创建虚拟DOM元素、比较差异并更新实际DOM,这可能会导致性能下降。这里我们需要了解 React 一些特性来优化:
- 物理上可以:分页和无限滚动加载,减少一次性加载的数据量。
- 使用 memo api 来优化重复重复渲染。
- 渲染可见部分的数据,而不是全部的数据,例如虚拟列表组件
- 其他的
四、复杂的动画
动画一般都会涉及到元素的重绘和重排,这些操作会导致浏览器重新渲染页面元素。
- 重绘:在不改变元素的布局的情况下,更新元素的可见样式(颜色、背景)
- 重排:改变元素的布局,可能导致整个元素的重新布局。
它们都会影响性能的开销,如通过虚拟 dom 进行修改,虚拟 dom 需要额外的自己的与运行时,增加额外的性能。
- React 渲染
ts
import React, { useState } from 'react';
function AnimatedComponent() {
const [left, setLeft] = useState(0);
const animateLeft = () => {
setLeft(left + 10);
};
return (
<div>
<button onClick={animateLeft}>Animate</button>
<div
style={{
width: '100px',
height: '100px',
backgroundColor: 'blue',
transform: `translateX(${left}px)`,
}}
/>
</div>
);
}
export default AnimatedComponent;
- 原生 dom 渲染
ts
const box = document.getElementById('box');
let left = 0;
document.getElementById('animateButton').addEventListener('click', () => {
left += 10;
box.style.transform = `translateX(${left}px)`;
});
React 渲染需要状态配合,计算整个 style 当前值,会影响 dom 的内容,明显此时的性能会稍微有所下降。
五、低级的 dom 控制
直接访问和操作底层DOM元素的情况,而虚拟DOM库通常会将这些细节进行抽象和封装。在这些情况下,直接使用原生DOM操作通常更合适,因为它提供了更大的灵活性。
ts
// 获取一个DOM元素
const element = document.getElementById('myElement');
// 直接操作DOM,例如添加一个事件处理程序
element.addEventListener('click', () => {
element.style.backgroundColor = 'red';
});
// 手动创建新的DOM元素
const newElement = document.createElement('div');
newElement.textContent = 'This is a new element';
// 插入新元素到DOM中
document.body.appendChild(newElement);
// 移除DOM元素
const elementToRemove = document.getElementById('elementToRemove');
if (elementToRemove) {
elementToRemove.parentNode.removeChild(elementToRemove);
}
此处挂载节点:
- appendChild 添加到元素到指定 dom 种。
- 从父接节点 parentNode.removeChild 移除一个元素
六、虚拟 dom 的优势
虚拟 dom 优势在于:
- 优化、批量处理更新以及减少重排和重绘等性能。
- 组件化开发
- 跨平台兼容性
- 生态系统和社区支持
八、原生 dom 优势
- 学习曲线
- 直接控制
- 适合小型项目
- 性能优势
- 定制需求
七、小结
虚拟 dom 自身的优点是适合工程化组件化开发的大型项目,但是存在 运行时
, 这些虚拟 dom 一般都有自己的运行时代码,在一些小的 dom 操作时不占据优势,在一些大的项目中虚拟 dom 配合组件化开发的优势得以体现,尽管虚拟 dom 可以优化,但是原生 dom 直接操作的能力,使得其性能会下降,在类似页面列表渲染种,使用虚拟列表,展示可见部分,或者分页等物理方式进行优化。