javascript --- 将DOM结构转换成虚拟DOM 虚拟DOM转换成真实的DOM结构

虚拟DOM的实现

使用虚拟DOM的原因: 减少回流与重绘

将DOM结构转换成对象保存到内存中

<img /> => { tag: 'img'}

文本节点 => { tag: undefined, value: '文本节点' }

<img title="1" class="c" /> => { tag: 'img', data: { title = "1", class="c" } }

<div><img /></div> => { tag: 'div', children: [{ tag: 'div' }]}

根据上面可以写出虚拟DOM的数据结构

class VNode {constructor(tag, data, value, type) {this.tag = tag && tag.toLowerCase()this.data = datathis.value = valuethis.type = typethis.children = []}appendChild(vnode){this.children.push(vnode)}
}

可能用到的基础知识

  • 判断元素的节点类型: node.nodeType
let nodeType = node.nodeType
if(nodeType == 1) {// 元素类型
} else if (nodeType == 3) {// 节点类型
}
  • 获取元素类型的标签名和属性 && 属性中具体的键值对,保存在一个对象中
let nodeName = node.nodeName	// 标签名
let attrs  = node.attributes	// 属性
let _attrObj = {}	// 保存各个具体的属性的键值对,相当于虚拟DOM中的data属性
for(let i =0, len = attrs.length; i< len; i++){_attrObj[attrs[i].nodeName] = attrs[i].nodeValue
}
  • 获取当前节点的子节点
let childNodes = node.childNodes
for(let i = 0, len = childNodes.length; i < len; i++){console.log(childNodes[i])
}

算法思路

  • 使用document.querySelector获取要转换成虚拟DOM的模板
  • 使用nodeType方法来获取是元素类型还是文本类型
  • 若是元素类型
    • 使用nodeName获取标签名
    • 使用attributes获取属性名,并将具体的属性保存到一个对象_attrObj
    • 创建虚拟DOM节点
    • 考虑元素类型是否有子节点,使用递归,将子节点的虚拟DOM存入其中
  • 若是文本类型
    • 直接创建虚拟DOM,不需要考虑子节点的问题
// 虚拟DOM的数据结构
class VNode{constrctor(tag, data, value, type){this.tag = tag && tag.toLowerCase()this.data = datathis.value = valuethis.type = typethis.children = []}appendChild(vnode) {this.children.push(vnode)}
}// 获取要转换的DOM结构
let root = document.querySelector('#root')
// 使用getVNode方法将 真实的DOM结构转换成虚拟DOM
let vroot = getVNode(root)	

以上写了虚拟DOM的数据结构,以及使用getVNode方法将真实DOM结构转换成虚拟DOM,下面开始逐步实现getVNode方法

  • 判断节点类型,并返回虚拟DOM
function getVNode(node){// 获取节点类型let nodeType = node.nodeType;if(nodeType == 1){// 元素类型: 获取其属性,判断子元素,创建虚拟DOM} else if(nodeType == 3) {// 文本类型: 直接创建虚拟DOM}let _vnode = null;return _vnode
}
  • 下面根据元素类型和文本类型分别创建虚拟DOM
if(nodeType == 1){// 标签名let tag = node.nodeName// 属性let attrs = node.attributes/*属性转换成对象形式: <div title ="marron" class="1"></div>{ tag: 'div', data: { title: 'marron', class: '1' }}*/let _data = {};   // 这个_data就是虚拟DOM中的data属性for(let i =0, len = attrs.length; i< attrs.len; i++){_data[attrs[i].nodeName] = attrs[i].nodeValue}// 创建元素类型的虚拟DOM_vnode = new VNode(tag, _data, undefined, nodeType)// 考虑node的子元素let childNodes = node.childNodesfor(let i =0, len = childNodes.length; i < len; i++){_vnode.appendChild(getVNode(childNodes[i]))}
}
// 接下来考虑文本类型
else if(nodeType == 3){_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)
}

总体代码

class VNode {constructor(tag, data, value, type) {this.tag = tag && tag.toLowerCase()this.data = datathis.value = valuethis.type = typethis.children = []}appendChild(vnode){this.children.push(vnode)}
}function getVNode(node) {let nodeType = node.nodeTypelet _vnode = nullif (nodeType == 1) {let tag = node.nodeNamelet attrs = node.attributeslet _data = {}for (let i = 0, len = attrs.length; i < len; i++) {_data[attrs[i].nodeName] = attrs[i].nodeValue}_vnode = new VNode(tag, _data, undefined, nodeType)let childNodes = node.childNodesfor (let i = 0, len = childNodes.length; i < len; i++) {_vnode.appendChild(getVNode(childNodes[i]))}} else if (nodeType == 3) {_vnode = new VNode(undefined, undefined, node.nodeValue, nodeType)}return _vnode
}let root = document.querySelector('#root')
let vroot = getVNode(root)	
console.log(vroot)

将虚拟DOM转换成真实的DOM结构

此过程就是上面的反过程

可能用到的知识点

  • 创建文本节点
document.createTextNode(value)
  • 创建元素节点
document.createElement(tag)
  • 给元素节点添加属性
node.setAttribute(attrName, attrValue)
  • 给元素节点添加子节点
node.appendChild(node)

算法思路

  • 虚拟DOM的结构中,元素的节点类型存储在type中,根据type可以判断出是文本节点还是元素节点
  • 若为文本节点,直接返回一个文本节点return document.createTextNode(value)
  • 若为元素节点
    • 创建一个node节点:_node = document.createElement(tag)
    • 遍历虚拟DOM中的data属性,将其中的值赋给node节点
    • 给当前节点添加子节点

具体实现

function parseVNode(vnode){let type = vnode.typelet _node = nullif(type == 3){return document.createTextNode(vnode.value)} else if (type == 1){_node = document.createElement(vnode.tag)let data = vnode.datalet attrName,attrValueObject.keys(data).forEach(key=>{attrName = keyattrValue = data[key]_node.setAttribute(attrName, attrValue)})// 考虑子元素let children = vnode.childrenchildren.forEach( subvnode =>{_node.appendChild(parseVNode(subvnode))})}return _node
}

验证:

let root = querySelector('#root')
let vroot = getVNode(root)
console.log(vroot)
let root1 = parseVNode(vroot)
console.log(root1)

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

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

相关文章

swap(a,b)值交换的4种方法

方法一&#xff1a;int tmp 0; tmp b;b a; a tmp; 方法二&#xff1a;a ab; b a-b; a a-b;方法三&#xff1a;a ^ b ^ a^ b;方法四&#xff1a;a ab-(ba);转载于:https://www.cnblogs.com/vocaloid01/p/9514126.html

装系统工具

安装如果失败,注意是不是工具的版本太老导致 系统分区工具: DiskGeniusPortable 刻录工具: UlraISO rufus https://rufus.ie/ win32diskimager 转载于:https://www.cnblogs.com/jiangfeilong/p/9937164.html

小程序WXML基本使用

数据绑定 <!--wxml--> <view> {{message}} </view> // page.js Page({data: {message: Hello MINA!} }) 列表渲染 <!--wxml--> <view wx:for"{{array}}"> {{item}} </view> // page.js Page({data: {array: [1, 2, 3, 4, 5]} })…

javascript --- vue中简单的模板渲染

一层的渲染 将下面的模板中的mustache语法使用给定数据渲染. 模板如下 <div id"root"><div><div><p>{{name}} - {{message}}</p></div></div><p>{{name}}</p><p>{{msg}}</p> </div>数据如…

tomcat 虚拟路径 与 虚拟主机配置

虚拟路径配置 方法一&#xff1a;此方法需要重启服务 打开下面文件 在host里面添加context标签 <Context docBase"D:\test" path"/testServlet/aaaaa" reloadable"true" /> 浏览器访问&#xff1a;http://172.16.6.103:1080/testServlet/a…

20172328 2018-2019《Java软件结构与数据结构》第八周学习总结

20172328 2018-2019《Java软件结构与数据结构》第八周学习总结 概述 Generalization 本周学习了二叉树的另一种有序扩展&#xff1f;是什么呢&#xff1f;你猜对了&#xff01;ヾ(◍∇◍)&#xff89;&#xff9e;就是堆。本章将讲解堆的链表实现and数组实现&#xff0c;以及往…

javascript --- 函数的柯里化 Vue 2.x中柯里化的使用

函数式编程部分重点 参考资料: 函数式编程 柯里化 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数 var add function (x) {return function(y) {return x y} }var increment add(1) var addTen add(10)increment(2) // 3addTen(10) // 12判断元素:V…

MYSQL重置ROOT密码

背景 mysql 服务器长时间未使用&#xff0c;管理员当时设置的root 密码忘记&#xff0c;需要重置 root 密码&#xff0c;并加以妥善保存。 步骤 关闭 mysql 服务以跳过密码验证的方式启动 mysql 服务mysqld --skip-grant-tables本地登陆后设置新的root 密码 update mysql.user …

javascript --- Vue初始化 模板渲染

不带响应式的Vue缩减实现 模板 现有模板如下: <div id "app"><div class"c1"><div titlett1 id"id">{{ name }}</div><div titlett2 >{{age}}</div><div>hello3</div></div><ul>…

#RANK_1 极其简单的递归——骑士与金币

2000:金币 总时间限制: 1000ms内存限制: 65536kB描述国王将金币作为工资&#xff0c;发放给忠诚的骑士。第一天&#xff0c;骑士收到一枚金币&#xff1b;之后两天&#xff08;第二天和第三天&#xff09;里&#xff0c;每天收到两枚金币&#xff1b;之后三天&#xff08;第四、…

动手动脑4

import java.io.*; public class ThrowMultiExceptionsDemo { public static void main(String[] args) { try { throwsTest(); } catch(IOException e) { System.out.println("捕捉异常"); }}private static void throwsTest() throws ArithmeticException,IOExcep…

javascript --- 对象原型

对象原型 参考 - MDN Javascript中的原型 在Javascript中,每一个函数都有一个特殊的属性,叫做原型 下面获取函数的原型fn.prototype function f1(){} console.log(f1.prototype) /*{constructor: f f1()__proto__:{constructor: f Object()__defineGetter__: f __defineGe…

从零认识单片机(9)

keil软件&#xff1a; IDE:IDE是集成开发环境&#xff0c;就是用来开发的完整的软件系统。 keil和mdk: keil:只能用来开发单片机 mdk:基于keil 拓展ARM的开发&#xff0c;主要用来开发ARM-cortex-m系列单片机的程序。 使用keil打开已有的工程项目&#xff1a; 1、IDE开发软件&a…

javascript --- vue2.x中原型的使用(拦截数组方法) 响应式原理(部分)

说明 在Vue2.x中,利用了对原型链的理解,巧妙的利用JavaScript中的原型链,实现了数组的pop、push、shift、unshift、reverse、sort、splice等的拦截. 你可能需要的知识 参考 - MDN 原型链 JavaScript常被描述为一种基于原型的语言(prototype-based language),每个对象拥有一…

dubbo-admin构建报错

dubbo-admin构建报错 意思是maven库里没有dubbo2.5.4-SNAPSHOT.jar这个版本的dubbo的jar包&#xff0c;把dubbo-admin项目的pom.xml的   <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${proje…

javascript --- 手写Promise、快排、冒泡、单例模式+观察者模式

手写promise 一种异步的解决方案, 参考 Promise代码基本结构 function Promise(executor){this.state pending;this.value undefined;this.reason undefined;function resolve(){}function reject(){} } module.exports Promisestate保存的是当前的状态,在Promise状态发…

PyCharm 通过Github和Git上管理代码

1.Pycharm中设置如图: 2.配置Git,通过网页 https://www.git-scm.com/download/win 下载 3. 转载于:https://www.cnblogs.com/0909/p/9956406.html

【BZOJ】2395: [Balkan 2011]Timeismoney

题解 最小乘积生成树&#xff01; 我们把&#xff0c;x的总和和y的总和作为x坐标和y左边&#xff0c;画在坐标系上 我们选择两个初始点&#xff0c;一个是最靠近y轴的A&#xff0c;也就是x总和最小&#xff0c;一个是最靠近x轴的B&#xff0c;也就是y总和最小 连接两条直线&…

http --- http与https相关概念小结

网络协议 参考 HTTP的特性 HTTP协议构建于TCP/IP协议之上,是一个应用层协议,默认端口是80HTTP是无连接无状态的 HTTP报文 请求报文 HTTP协议是以ASCII码传输,建立在 TCP/IP 协议之上的应用层规范。规范把HTTP请求分为三个部分:状态行、请求头、消息主体。 <method>…

Spring AOP注解方式实现

简介 上文已经提到了Spring AOP的概念以及简单的静态代理、动态代理简单示例&#xff0c;链接地址&#xff1a;https://www.cnblogs.com/chenzhaoren/p/9959596.html 本文将介绍Spring AOP的常用注解以及注解形式实现动态代理的简单示例。 常用注解 aspect&#xff1a;定义切面…