热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

normalize函数_关于实现一个函数把真实dom转换成虚拟dom原来是这么一回事

实现这个函数之前,首先要对js的一些官方api要有明确概念。document.querySelector()HTMLElementHTMLDOMnodeType属性H

实现这个函数之前,首先要对js的一些官方api要有明确概念。

  • document.querySelector()

  • HTMLElement

  • HTML DOM nodeType 属性

HTML DOM Element 对象

HTML DOM 节点

在 HTML DOM (文档对象模型)中,每个部分都是节点:

  • 文档本身是文档节点
  • 所有 HTML 元素是元素节点
  • 所有 HTML 属性是属性节点
  • HTML 元素内的文本是文本节点
  • 注释是注释节点

Element 对象

在 HTML DOM 中,Element 对象表示 HTML 元素。

Element 对象可以拥有类型为元素节点、文本节点、注释节点的子节点。

NodeList 对象表示节点列表,比如 HTML 元素的子节点集合。

元素也可以拥有属性。属性是属性节点(参见下一节)。

下面的属性和方法可用于所有 HTML 元素上:

属性 / 方法描述
element.accessKey设置或返回元素的快捷键。
element.appendChild()向元素添加新的子节点,作为最后一个子节点。
element.attributes返回元素属性的 NamedNodeMap。
element.childNodes返回元素子节点的 NodeList。
element.className设置或返回元素的 class 属性。
element.clientHeight返回元素的可见高度。
element.clientWidth返回元素的可见宽度。
element.cloneNode()克隆元素。
element.compareDocumentPosition()比较两个元素的文档位置。
element.contentEditable设置或返回元素的文本方向。
element.dir设置或返回元素的内容是否可编辑。
element.firstChild返回元素的首个孩子。
element.getAttribute()返回元素节点的指定属性值。
element.getAttributeNode()返回指定的属性节点。
element.getElementsByTagName()返回拥有指定标签名的所有子元素的集合。
element.getFeature()返回实现了指定特性的 API 的某个对象。
element.getUserData()返回关联元素上键的对象。
element.hasAttribute()如果元素拥有指定属性,则返回true,否则返回 false。
element.hasAttributes()如果元素拥有属性,则返回 true,否则返回 false。
element.hasChildNodes()如果元素拥有子节点,则返回 true,否则 false。
element.id设置或返回元素的 id。
element.innerHTML设置或返回元素的内容。
element.insertBefore()在指定的已有的子节点之前插入新节点。
element.isContentEditable设置或返回元素的内容。
element.isDefaultNamespace()如果指定的 namespaceURI 是默认的,则返回 true,否则返回 false。
element.isEqualNode()检查两个元素是否相等。
element.isSameNode()检查两个元素是否是相同的节点。
element.isSupported()如果元素支持指定特性,则返回 true。
element.lang设置或返回元素的语言代码。
element.lastChild返回元素的最后一个子元素。
element.namespaceURI返回元素的 namespace URI。
element.nextSibling返回位于相同节点树层级的下一个节点。
element.nodeName返回元素的名称。
element.nodeType返回元素的节点类型。
element.nodeValue设置或返回元素值。
element.normalize()合并元素中相邻的文本节点,并移除空的文本节点。
element.offsetHeight返回元素的高度。
element.offsetWidth返回元素的宽度。
element.offsetLeft返回元素的水平偏移位置。
element.offsetParent返回元素的偏移容器。
element.offsetTop返回元素的垂直偏移位置。
element.ownerDocument返回元素的根元素(文档对象)。
element.parentNode返回元素的父节点。
element.previousSibling返回位于相同节点树层级的前一个元素。
element.removeAttribute()从元素中移除指定属性。
element.removeAttributeNode()移除指定的属性节点,并返回被移除的节点。
element.removeChild()从元素中移除子节点。
element.replaceChild()替换元素中的子节点。
element.scrollHeight返回元素的整体高度。
element.scrollLeft返回元素左边缘与视图之间的距离。
element.scrollTop返回元素上边缘与视图之间的距离。
element.scrollWidth返回元素的整体宽度。
element.setAttribute()把指定属性设置或更改为指定值。
element.setAttributeNode()设置或更改指定属性节点。
element.setIdAttribute()
element.setIdAttributeNode()
element.setUserData()把对象关联到元素上的键。
element.style设置或返回元素的 style 属性。
element.tabIndex设置或返回元素的 tab 键控制次序。
element.tagName返回元素的标签名。
element.textContent设置或返回节点及其后代的文本内容。
element.title设置或返回元素的 title 属性。
element.toString()把元素转换为字符串。
nodelist.item()返回 NodeList 中位于指定下标的节点。
nodelist.length返回 NodeList 中的节点数。
Node Types

文档、元素、属性以及 HTML 或 XML 文档的其他方面拥有不同的节点类型。

存在 12 种不同的节点类型,其中可能会有不同节点类型的子节点:

节点类型描述子节点
1Element代表元素Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference
2Attr代表属性Text, EntityReference
3Text代表元素或属性中的文本内容。None
4CDATASection代表文档中的 CDATA 部分(不会由解析器解析的文本)。None
5EntityReference代表实体引用。Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
6Entity代表实体。Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
7ProcessingInstruction代表处理指令。None
8Comment代表注释。None
9Document代表整个文档(DOM 树的根节点)。Element, ProcessingInstruction, Comment, DocumentType
10DocumentType向为文档定义的实体提供接口None
11DocumentFragment代表轻量级的 Document 对象,能够容纳文档的某个部分Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference
12Notation代表 DTD 中声明的符号。None

节点类型 - 返回值

对于每种节点类型,nodeName 和 nodeValue 属性的返回值:

节点类型nodeName 返回nodeValue 返回
1Element元素名null
2Attr属性名称属性值
3Text#text节点的内容
4CDATASection#cdata-section节点的内容
5EntityReference实体引用名称null
6Entity实体名称null
7ProcessingInstructiontarget节点的内容
8Comment#comment注释文本
9Document#documentnull
10DocumentType文档类型名称null
11DocumentFragment#document 片段null
12Notation符号名称null
HTML DOM querySelector() 方法

语法

document.querySelector(CSS selectors)

参数值

参数类型描述
CSS 选择器String必须。指定一个或多个匹配元素的 CSS 选择器。可以使用它们的 id, 类, 类型, 属性, 属性值等来选取元素。 对于多个选择器,使用逗号隔开,返回一个匹配的元素。 提示: 更多 CSS 选择器,请参阅 CSS 选择器参考手册。
真实dom转换成虚拟dom

思路

首先要明确,虚拟dom树上的一个节点VNode,需要哪些必要的数据。

  • 标签名
  • 元素类型
  • 元素属性值(id、class、name等,字典形式)
  • 子节点(数组形式)
  • 文本

所以,我们的一个VNode节点,可以设置成一个这样的数据结构,这里用es6的class语法来实现。

class VNode {
    constructor(tag, attrs, value, type) {
        this.tag = tag && tag.toLowerCase(); // 标签名
        this.attrs = attrs;  // 属性值
        this.value = value; // 文本
        this.type = type;  // 元素类型
        this.children = []  // 子节点
    }

    // 追加子节点
    appendChild(vnode) {
        this.children.push(vnode)
    }

}

然后,上文中的node type告诉我们:

文档、元素、属性以及 HTML 或 XML 文档的其他方面拥有不同的节点类型。

存在 12 种不同的节点类型,其中可能会有不同节点类型的子节点

所以判断一个node是文本节点还是元素结点时,可以用node.nodeType == 1node.nodeType == 3

那么接下来就很简单了,无非就是遍历一棵n叉树,使用递归就很容易实现,只是在递归的时候,加入node.nodeType == 1node.nodeType == 3这两个判断,然后遇到文本情况跳出递归栈就可以了。

再细化到每个节点的拷贝,就是针对上面提到的几个必要属性,值得注意的是,属性也是一个节点。从官方api中拿数据填到vnode节点中,这个就是一个简单的属性深拷贝,这里就不再赘述了,直接上代码。

function getVNode(node) {
    let nodeType = node.nodeType;
    let _vnode = null;

    // 对节点进行判断
    if (nodeType === 1) {
        // 元素节点
        let nodeName = node.nodeName;

        // 属性,返回属性组成的数组,我们就是把这个伪数组转换为对象
        let attrs = node.attributes

        let _attrObj = {};

        // 循环attrs
        for (let i = 0; i             // attrs[i]是一个属性节点,我们要的是nodeName这个属性
            _attrObj[attrs[i].nodeName] = attrs[i].nodeValue
        }

        _vnode = new VNode(nodeName, _attrObj, undefined, nodeType);


        // 考虑node的子元素
        let childNodes = node.childNodes;
        for (let i = 0; i             //递归
            _vnode.appendChild(getVNode(childNodes[i]));
        }
    } else if (nodeType === 3) {
        // 文本节点
        _vnode = new VNode(undefined, undefined, node.nodeValue, nodeType);
    }
    return _vnode;
}

注意点:文本节点的判断和节点文本的获取

由于文本也是一个节点。

**元素本身的nodeValue本来就是null,nodeValue是针对#text的。而且最重要的是nodeValue属性是用来获取文本节点的值的。**这也就意味着,nodeValue拿不到非文本节点的值。

一般情况下,打印文本节点,会出现#text字样,这就是在console里辨别出文本节点的其中一个方法。

ae68e7df7d173b4ccded8dd57c6231ed.png
img

完整代码

html>


    
    Titletitle>
head>


    hello1div>
    hello2div>
    hello3div>
    

    


        1span>2span>3span>4span>5span>6span>
    p>
    


            
  • 1li>
            
  • 2li>
            
  • 3li>
            
  • 4li>
            
  • 5li>
        ul>
    div>
    // 用内存去表述DOM// 将真实DOM转化为虚拟DOM// // 文本节点 => {tag:undefined,value:'文本节点'}   文本节点转化// // // 用构造函数来 进行以上转换class VNode {constructor(tag, attrs, value, type) {this.tag = tag && tag.toLowerCase(); // 标签名this.attrs = attrs;  // 属性值this.value = value; // 文本this.type = type;  // 元素类型this.children = []  // 子节点
            }// 追加子节点
            appendChild(vnode) {this.children.push(vnode)
            }
        }//兼容浏览器获取节点文本的方法function getTextFromNode(e) {let t = "";//如果传入的是元素,则继续遍历其子元素//否则假定它是一个数组
            e = e.childNodes || e;//遍历所有子节点for (let j = 0; j             //如果不是元素,追加其文本值//否则,递归遍历所有元素的子节点
                t += e[j].nodeType !== 1 ?
                    e[j].nodeValue : getTextFromNode(e[j].childNodes);
            }//返回区配的文本return t;
        }function getVirtualDom(node) {let nodeType = node.nodeType;let _vnode = null;if (nodeType === 1) {let nodeName = node.nodeName;let property = node.attributes;let _propertyObj = {};for (let i = 0; i                 _propertyObj[property[i].nodeName] = property[i].nodeValue;
                }
                _vnode = new VNode(nodeName, _propertyObj, undefined, nodeType)let childNodes = node.childNodes;for (let i = 0; i                 _vnode.appendChild(getVirtualDom(childNodes[i]))
                }
            } else if (nodeType === 3) {// 尤其要注意,文本也是一个节点// 可以回忆一下,原生js生成一个节点的步骤,就理解了let text = node.nodeValue
                _vnode = new VNode(undefined, undefined, text, nodeType)
            }return _vnode;
        }let root = document.querySelector("#root");console.log(root);console.log(root.nodeType);console.log(root.attributes)// let vroot = getVNode(root);let vRoot = getVirtualDom(root)// console.log(vroot);console.log(vRoot)script>
    body>
    html>

    部分输出如下:

    7388f582136abf34b2d210d7a5175fdd.png
    img



推荐阅读
  • 本文全面解析了JavaScript中的DOM操作,并提供了详细的实践指南。DOM节点(Node)通常代表一个标签、文本或HTML属性,每个节点都具有一个nodeType属性,用于标识其类型。文章深入探讨了DOM节点的创建、查询、修改和删除等操作,结合实际案例,帮助读者更好地理解和掌握DOM编程技术。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
  • SecureCRT是一款功能强大的终端仿真软件,支持SSH1和SSH2协议,适用于在Windows环境下高效连接和管理Linux服务器。该工具不仅提供了稳定的连接性能,还具备丰富的配置选项,能够满足不同用户的需求。通过SecureCRT,用户可以轻松实现对远程Linux系统的安全访问和操作。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 思科IOS XE与ISE集成实现TACACS认证配置
    本文详细介绍了如何在思科IOS XE设备上配置TACACS认证,并通过ISE(Identity Services Engine)进行用户管理和授权。配置包括网络拓扑、设备设置和ISE端的具体步骤。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 在什么情况下MySQL的可重复读隔离级别会导致幻读现象? ... [详细]
  • 深入解析Android GPS机制:第五部分 ... [详细]
  • 【实例简介】本文详细介绍了如何在PHP中实现微信支付的退款功能,并提供了订单创建类的完整代码及调用示例。在配置过程中,需确保正确设置相关参数,特别是证书路径应根据项目实际情况进行调整。为了保证系统的安全性,存放证书的目录需要设置为可读权限。值得注意的是,普通支付操作无需证书,但在执行退款操作时必须提供证书。此外,本文还对常见的错误处理和调试技巧进行了说明,帮助开发者快速定位和解决问题。 ... [详细]
  • Hyperledger Fabric 1.4 节点 SDK 快速入门指南
    本文将详细介绍如何利用 Hyperledger Fabric 1.4 的 Node.js SDK 开发应用程序。通过最新版本的 Fabric Node.js SDK,开发者可以更高效地构建和部署基于区块链的应用,实现数据的安全共享和交易处理。文章将涵盖环境配置、SDK 安装、示例代码以及常见问题的解决方法,帮助读者快速上手并掌握核心功能。 ... [详细]
  • 如何使用 `org.eclipse.rdf4j.query.impl.MapBindingSet.getValue()` 方法及其代码示例详解 ... [详细]
author-avatar
手机用户2502884057
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有