JavaScript系列(40)--虚拟DOM实现详解

news2025/1/27 0:02:04

JavaScript虚拟DOM实现详解 🌳

今天,让我们深入了解虚拟DOM的实现原理,这是现代前端框架中非常重要的一个概念,它通过最小化实际DOM操作来提升应用性能。

虚拟DOM基础概念 🌟

💡 小知识:虚拟DOM是真实DOM的JavaScript对象表示,通过比较新旧虚拟DOM的差异来最小化实际DOM操作,从而提升性能。

基本实现 📊

// 1. 虚拟DOM节点实现
class VNode {
    constructor(type, props, children) {
        this.type = type;        // 节点类型
        this.props = props;      // 节点属性
        this.children = children;// 子节点
        this.key = props?.key;   // 用于优化的key
    }
}

// 2. 创建虚拟DOM的辅助函数
function createElement(type, props = {}, ...children) {
    return new VNode(
        type,
        props,
        children.flat().map(child =>
            typeof child === 'object' ? child : createTextNode(child)
        )
    );
}

function createTextNode(text) {
    return new VNode('TEXT_ELEMENT', { nodeValue: text }, []);
}

// 3. 虚拟DOM渲染器
class VDOMRenderer {
    constructor(container) {
        this.container = container;
    }
    
    render(vnode) {
        this.container.innerHTML = '';
        const element = this.createDOMElement(vnode);
        this.container.appendChild(element);
    }
    
    createDOMElement(vnode) {
        if (vnode.type === 'TEXT_ELEMENT') {
            return document.createTextNode(vnode.props.nodeValue);
        }
        
        const element = document.createElement(vnode.type);
        
        // 设置属性
        this.updateProps(element, vnode.props);
        
        // 递归创建子节点
        vnode.children.forEach(child => {
            element.appendChild(this.createDOMElement(child));
        });
        
        return element;
    }
    
    updateProps(element, props) {
        if (!props) return;
        
        Object.entries(props).forEach(([key, value]) => {
            if (key === 'key') return;
            
            if (key.startsWith('on')) {
                element.addEventListener(
                    key.toLowerCase().slice(2),
                    value
                );
            } else if (key === 'style' && typeof value === 'object') {
                Object.assign(element.style, value);
            } else if (key === 'className') {
                element.setAttribute('class', value);
            } else {
                element.setAttribute(key, value);
            }
        });
    }
}

高级功能实现 🚀

// 1. 虚拟DOM差异比较算法
class VDOMDiffer {
    diff(oldVNode, newVNode) {
        if (!oldVNode) {
            return { type: 'CREATE', newVNode };
        }
        
        if (!newVNode) {
            return { type: 'REMOVE' };
        }
        
        if (this.hasChanged(oldVNode, newVNode)) {
            return { type: 'REPLACE', newVNode };
        }
        
        if (newVNode.type !== 'TEXT_ELEMENT') {
            const patches = this.diffChildren(oldVNode, newVNode);
            const props = this.diffProps(oldVNode.props, newVNode.props);
            
            if (patches.length || props) {
                return {
                    type: 'UPDATE',
                    props,
                    patches
                };
            }
        }
        
        return null;
    }
    
    hasChanged(oldVNode, newVNode) {
        return typeof oldVNode !== typeof newVNode ||
               (oldVNode.type === 'TEXT_ELEMENT' && 
                oldVNode.props.nodeValue !== newVNode.props.nodeValue) ||
               oldVNode.type !== newVNode.type;
    }
    
    diffProps(oldProps, newProps) {
        const patches = {};
        let hasPatches = false;
        
        // 检查更新和新增的属性
        for (const [key, value] of Object.entries(newProps || {})) {
            if (oldProps?.[key] !== value) {
                patches[key] = value;
                hasPatches = true;
            }
        }
        
        // 检查删除的属性
        for (const key in oldProps || {}) {
            if (!(key in newProps)) {
                patches[key] = null;
                hasPatches = true;
            }
        }
        
        return hasPatches ? patches : null;
    }
    
    diffChildren(oldVNode, newVNode) {
        const patches = [];
        const maxLength = Math.max(
            oldVNode.children.length,
            newVNode.children.length
        );
        
        for (let i = 0; i < maxLength; i++) {
            const childPatch = this.diff(
                oldVNode.children[i],
                newVNode.children[i]
            );
            if (childPatch) {
                patches.push({ index: i, ...childPatch });
            }
        }
        
        return patches;
    }
}

// 2. 组件生命周期管理
class Component {
    constructor(props) {
        this.props = props;
        this.state = {};
        this.isMount = false;
    }
    
    setState(newState) {
        this.state = { ...this.state, ...newState };
        this.update();
    }
    
    update() {
        const vnode = this.render();
        if (this.isMount) {
            this.componentWillUpdate();
            this.vdomRenderer.patch(this.vnode, vnode);
            this.componentDidUpdate();
        } else {
            this.componentWillMount();
            this.vdomRenderer.render(vnode);
            this.isMount = true;
            this.componentDidMount();
        }
        this.vnode = vnode;
    }
    
    // 生命周期钩子
    componentWillMount() {}
    componentDidMount() {}
    componentWillUpdate() {}
    componentDidUpdate() {}
    componentWillUnmount() {}
}

// 3. 事件系统实现
class EventSystem {
    constructor() {
        this.listeners = new Map();
    }
    
    addEvent(element, eventType, handler) {
        if (!this.listeners.has(element)) {
            this.listeners.set(element, new Map());
        }
        
        const elementListeners = this.listeners.get(element);
        if (!elementListeners.has(eventType)) {
            elementListeners.set(eventType, new Set());
            
            // 添加事件委托
            element.addEventListener(eventType, (event) => {
                this.handleEvent(element, eventType, event);
            });
        }
        
        elementListeners.get(eventType).add(handler);
    }
    
    removeEvent(element, eventType, handler) {
        const elementListeners = this.listeners.get(element);
        if (!elementListeners) return;
        
        const typeListeners = elementListeners.get(eventType);
        if (!typeListeners) return;
        
        typeListeners.delete(handler);
        
        if (typeListeners.size === 0) {
            elementListeners.delete(eventType);
        }
        
        if (elementListeners.size === 0) {
            this.listeners.delete(element);
        }
    }
    
    handleEvent(element, eventType, event) {
        const elementListeners = this.listeners.get(element);
        if (!elementListeners) return;
        
        const typeListeners = elementListeners.get(eventType);
        if (!typeListeners) return;
        
        typeListeners.forEach(handler => handler(event));
    }
}

性能优化技巧 ⚡

// 1. 批量更新处理
class BatchUpdateManager {
    constructor() {
        this.updates = new Set();
        this.isPending = false;
    }
    
    addUpdate(component) {
        this.updates.add(component);
        this.requestUpdate();
    }
    
    requestUpdate() {
        if (!this.isPending) {
            this.isPending = true;
            Promise.resolve().then(() => this.processBatch());
        }
    }
    
    processBatch() {
        const components = Array.from(this.updates);
        this.updates.clear();
        this.isPending = false;
        
        components.forEach(component => component.update());
    }
}

// 2. 虚拟DOM缓存
class VNodeCache {
    constructor() {
        this.cache = new Map();
    }
    
    getKey(vnode) {
        return JSON.stringify({
            type: vnode.type,
            props: vnode.props,
            children: vnode.children.map(child => this.getKey(child))
        });
    }
    
    set(vnode, element) {
        const key = this.getKey(vnode);
        this.cache.set(key, element);
    }
    
    get(vnode) {
        const key = this.getKey(vnode);
        return this.cache.get(key);
    }
    
    clear() {
        this.cache.clear();
    }
}

// 3. 性能监控
class VDOMPerformanceMonitor {
    constructor() {
        this.metrics = {
            renders: 0,
            diffs: 0,
            patches: 0,
            renderTime: 0,
            diffTime: 0,
            patchTime: 0
        };
    }
    
    startMeasure(operation) {
        return performance.now();
    }
    
    endMeasure(operation, startTime) {
        const duration = performance.now() - startTime;
        this.metrics[`${operation}Time`] += duration;
        this.metrics[`${operation}s`]++;
    }
    
    getMetrics() {
        return {
            ...this.metrics,
            averageRenderTime: this.metrics.renderTime / this.metrics.renders,
            averageDiffTime: this.metrics.diffTime / this.metrics.diffs,
            averagePatchTime: this.metrics.patchTime / this.metrics.patches
        };
    }
    
    reset() {
        Object.keys(this.metrics).forEach(key => {
            this.metrics[key] = 0;
        });
    }
}

最佳实践建议 💡

  1. 性能优化策略
// 1. 组件更新优化器
class ComponentOptimizer {
    shouldComponentUpdate(oldProps, newProps, oldState, newState) {
        // 浅比较props和state
        return !this.shallowEqual(oldProps, newProps) ||
               !this.shallowEqual(oldState, newState);
    }
    
    shallowEqual(obj1, obj2) {
        if (obj1 === obj2) return true;
        if (!obj1 || !obj2) return false;
        
        const keys1 = Object.keys(obj1);
        const keys2 = Object.keys(obj2);
        
        if (keys1.length !== keys2.length) return false;
        
        return keys1.every(key => obj1[key] === obj2[key]);
    }
}

// 2. 虚拟DOM优化器
class VDOMOptimizer {
    static optimizeVNode(vnode) {
        // 移除空属性
        if (vnode.props) {
            Object.keys(vnode.props).forEach(key => {
                if (vnode.props[key] === undefined ||
                    vnode.props[key] === null) {
                    delete vnode.props[key];
                }
            });
        }
        
        // 优化子节点
        if (vnode.children) {
            vnode.children = vnode.children
                .filter(child => child !== null && child !== undefined)
                .map(child => this.optimizeVNode(child));
        }
        
        return vnode;
    }
}

// 3. 内存管理器
class MemoryManager {
    constructor() {
        this.pool = new Map();
        this.maxPoolSize = 1000;
    }
    
    acquireVNode(type, props, children) {
        const key = this.getKey(type, props);
        const pool = this.pool.get(key) || [];
        
        if (pool.length > 0) {
            const vnode = pool.pop();
            vnode.props = props;
            vnode.children = children;
            return vnode;
        }
        
        return new VNode(type, props, children);
    }
    
    releaseVNode(vnode) {
        const key = this.getKey(vnode.type, vnode.props);
        const pool = this.pool.get(key) || [];
        
        if (pool.length < this.maxPoolSize) {
            pool.push(vnode);
            this.pool.set(key, pool);
        }
    }
    
    getKey(type, props) {
        return `${type}-${props?.key || ''}`;
    }
}

结语 📝

虚拟DOM是现代前端框架中非常重要的一个概念,通过本文,我们学习了:

  1. 虚拟DOM的基本概念和实现原理
  2. 差异比较算法的实现
  3. 组件生命周期管理
  4. 事件系统的实现
  5. 性能优化技巧

💡 学习建议:在实践中,要注意平衡虚拟DOM的更新粒度,避免不必要的重渲染。同时,要善用key属性来优化列表渲染性能。


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2283674.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

0 基础学运维:解锁 K8s 云计算运维工程师成长密码

前言&#xff1a;作为一个过来人&#xff0c;我曾站在技术的门槛之外&#xff0c;连电脑运行内存和内存空间都傻傻分不清&#xff0c;完完全全的零基础。但如今&#xff0c;我已成长为一名资深的k8s云计算运维工程师。回顾这段历程&#xff0c;我深知踏上这条技术之路的艰辛与不…

在 vscode + cmake + GNU 工具链的基础上配置 JLINK

安装 JLINK JLINK 官网链接 下载安装后找到安装路径下的可执行文件 将此路径添加到环境变量的 Path 中。 创建 JFlash 项目 打开 JFlash&#xff0c;选择新建项目 选择单片机型号 在弹出的窗口中搜索单片机 其他参数根据实际情况填写 新建完成&#xff1a; 接下来设置…

【全栈】SprintBoot+vue3迷你商城(9)

【全栈】SprintBootvue3迷你商城&#xff08;9&#xff09; 往期的文章都在这里啦&#xff0c;大家有兴趣可以看一下 后端部分&#xff1a; 【全栈】SprintBootvue3迷你商城&#xff08;1&#xff09; 【全栈】SprintBootvue3迷你商城&#xff08;2&#xff09; 【全栈】Spr…

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…

Fullcalendar @fullcalendar/react 样式错乱丢失问题和导致页面卡顿崩溃问题

问题描述&#xff1a; 我使用 fullcalendar的react版本时&#xff0c;出现了一个诡异的问题&#xff0c;当我切换到 一个iframe页面时&#xff08;整个页面是一个iframe嵌入的&#xff09;&#xff0c;再切换回来日历的样式丢失了&#xff01;不仅丢失了样式还导致页面崩溃了&…

dm8在Linux环境安装精简步骤说明(2024年12月更新版dm8)

dm8在Linux环境安装详细步骤 - - 2025年1月之后dm8 环境介绍1 修改操作系统资源限制2 操作系统创建用户3 操作系统配置4 数据库安装5 初始化数据库6 实例参数优化7 登录数据库配置归档与备份8 配置审计9 创建用户10 屏蔽关键字与数据库兼容模式11 jdbc连接串配置12 更多达梦数据…

S4 HANA更改Tax base Amount的字段控制

本文主要介绍在S4 HANA OP中Tax base Amount的字段控制相关设置。具体请参照如下内容&#xff1a; 1. 更改Tax base Amount的字段控制 以上配置用于控制FB60/FB65/FB70/FB75/MIRO的页签“Tax”界面是否可以修改“Tax base Amount”&#xff0c; 如果勾选Change 表示可以修改T…

JVM堆空间

一、堆空间的核心概述 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心区域。Java堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了。是JVM管理的最大一块内存空间。 堆内存的大小是可以调节的。堆可以处于物理上不连续的内存空间中&#xff…

《深入解析:DOS检测的技术原理与方法》

DDOS入侵检测与防御 一、实现Linux下DDOS的入侵检测与防御 利用Python编程实现对wrk的泛洪攻击检测&#xff0c;并让程序触发调用Linux命令实现防御: 1、泛洪攻击的检测&#xff0c;可以考虑使用的命令&#xff0c;这些命令可以通过Python进行调用和分析 (1) netstat -ant …

PID如何调试,如何配置P,I,D值,如何适配pwm的定时器配置,如何给小车配电源

首先你要搞清楚PID公式原理 PID算法解析PID算法解析_pid滤波算法-CSDN博客 然后你要明白调试原理 首先要确定一个电源 电源决定了你后面调试时电机转动速度大小和pwm占空比的关系&#xff0c;电源电压越大那要转到同一速度所需的占空比越小&#xff0c;反之电源电压越小那要…

小马模拟器-第三方全街机游戏模拟器

链接&#xff1a;https://pan.xunlei.com/s/VOHSiB6st-f3RWlIK01MS2fUA1?pwd44v7# 1.小马模拟器是一款完全免费的游戏模拟器软件&#xff0c;支持街机&#xff08;FBA,MAME,PGM2&#xff09;,3DS,WII,NGC,DC,SS,DOS,MD,WSC,NDS,JAVA,PCE,FC,SFC,GBA,GBC,PSP,PS,N64等多种游戏…

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…

【算法】递归型枚举与回溯剪枝初识

递归型枚举与回溯剪枝初识 1.枚举子集2.组合型枚举3.枚举排列4.全排列问题 什么是搜索&#xff1f;搜索&#xff0c;是一种枚举&#xff0c;通过穷举所有的情况来找到最优解&#xff0c;或者统计合法解的个数。因此&#xff0c;搜索有时候也叫作暴搜。搜索一般分为深度优先搜索…

rocketmq-product-send方法源码分析

先看有哪些send方法 首先说红圈的 有3个红圈。归类成3种发送方式。假设前提条件&#xff0c;发送的topic&#xff0c;有3个broker&#xff0c;每个broker总共4个write队列&#xff0c;总共有12个队列。 普通发送。负载均衡12个队列。指定超时时间指定MessageQueue,发送&#…

69.在 Vue 3 中使用 OpenLayers 拖拽实现放大区域的效果(DragPan)

引言 在现代 Web 开发中&#xff0c;地图功能已经成为许多应用的重要组成部分。OpenLayers 是一个功能强大的开源地图库&#xff0c;支持多种地图源和交互操作。Vue 3 是一个流行的前端框架&#xff0c;以其响应式数据和组件化开发著称。本文将介绍如何在 Vue 3 中集成 OpenLa…

77,【1】.[CISCN2019 华东南赛区]Web4

有句英文&#xff0c;看看什么意思 好像也可以不看 进入靶场 点击蓝色字体 我勒个豆&#xff0c;百度哇 所以重点应该在url上&#xff0c;属于任意文件读取类型 接下来该判断框架了 常见的web框架如下 一&#xff0c;Python 框架 1.Flask URL 示例 1&#xff1a;http://…

手撕B-树

一、概述 1.历史 B树&#xff08;B-Tree&#xff09;结构是一种高效存储和查询数据的方法&#xff0c;它的历史可以追溯到1970年代早期。B树的发明人Rudolf Bayer和Edward M. McCreight分别发表了一篇论文介绍了B树。这篇论文是1972年发表于《ACM Transactions on Database S…

一文简单回顾复习Java基础概念

还是和往常一样&#xff0c;我以提问的方式回顾复习&#xff0c;今天回顾下Java小白入门应该知道的一些基础知识 Java语言有哪些特点呢&#xff1f; Java语言的特点有&#xff1a; 面向对象&#xff0c;主要是封装、继承、多态&#xff1b;平台无关性&#xff0c;“一次编写…

GCC之编译(8)AR打包命令

GCC之(8)AR二进制打包命令 Author: Once Day Date: 2025年1月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章请查看专栏: Linux实践记录_Once-Day的博客-C…

2.1.3 第一个工程,点灯!

新建工程 点击菜单栏左上角&#xff0c;新建工程或者选择“文件”-“新建工程”&#xff0c;选择工程类型“标准工程”选择设备类型和编程语言&#xff0c;并指定工程文件名及保存路径&#xff0c;如下图所示&#xff1a; 选择工程类型为“标准工程” 选择主模块机型&#x…