虚拟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)

javascript --- 将DOM结构转换成虚拟DOM 虚拟DOM转换成真实的DOM结构相关推荐

  1. 虚拟dom_虚拟DOM与dom diff

    什么是虚拟DOM 虚拟DOM是一颗以JavaScript对象(node节点)作为基础的树,用对象属性来描述节点,他是对真实DOM的抽象,通过一些列操作使这棵树映射到真实环境上 虚拟dom就是能代表DO ...

  2. 虚拟dom_虚拟DOM发展的前世与今身

    web这几年蓬勃发展.经历了几个比较大的转变.我们先来大概回顾一下. Jquery 在虚拟Dom被提出来之前,我们前端框架Jquery凭借着良好的兼容性和简单易用的特性征服了大量的前端开发者,从而统治 ...

  3. CSS3新特性(属性选择器 、结构伪类选择器、2D/3D转换、动画、浏览器私有前缀)

    这里写目录标题 一.CSS3 属性选择器 二.CSS3 结构伪类选择器 三. CSS3 伪元素选择器 四.CSS3 2D转换 1.2D 转换之移动 translate 2.2D 转换之旋转 rotat ...

  4. wps html转换成pdf文件,wps怎么转换成pdf

    身为一名办公族+策划狗,平时免不了经常要和Word.Excel.PPT办公三件套打交道.这其中最常用到的就是Word了,你懂的,写策划方案嘛.而写完策划方案后,需要将这份方案发给领导审批,通常我会直接 ...

  5. CAJ文献如何转成PDF?免费全篇转换的方法

    对于CAJ的阅读和修改相对其他常用文档会复杂一些,所以我们经常都是将CAJ转换成PDF.花钱的转换工具有很多,但其实免费也能将整篇CAJ文献完整转换哦. 方法1: 操作PDF格式比较多的话,对于PDF ...

  6. 使用sed,awk将love转换成LOVE,将CHINA转换成china

    将love转换成LOVE,将CHINA转换成china echo "love CHINA" | sed -e 's/love/LOVE/' -e 's/CHINA/china/' ...

  7. java 首字母小写_java实现将字符串中首字母转换成大写,其它全部转换成小写的方法示例...

    本文实例讲述了java实现将字符串中首字母转换成大写,其它全部转换成小写的方法.分享给大家供大家参考,具体如下: public class TestSubstring { public static ...

  8. 在asp.net中做视频转换,将各种视频文件转换成.flv格式

    首先,我们部署一下文件夹.在工程的目录下新建几个文件夹如下图: UpFiles文件夹是要保存你上传的文件,PlayFiles文件夹是用于你转换后保存的文件(用于网上播放) ImgFile文件夹是保存截 ...

  9. 一个物理CPU如何划分成多个虚拟CPU

    原文链接:一个物理CPU如何划分成多个虚拟CPU 问题:一个虚拟机可以分配多少个虚拟CPU,以及如何在服务器上限制虚拟机的数量? 一个物理CPU一般一个内核会支持多个处理线程(英特尔超线程技术).这就 ...

最新文章

  1. Linux命令:MySQL系列之十--MySQL用户和权限管理,mysql管理员密码重置
  2. 教你在Excel里做GA的水平百分比图的详细步骤(图文教程)-成为excel大师(1)...
  3. Gradient Boosted Decision Trees详解
  4. Dijkstra算法——计算一个点到其他所有点的最短路径的算法
  5. 《Spring Recipes》第二章笔记:Creating Beans by Invokin...
  6. 20145203盖泽双《网络对抗技术》拓展:注入:shellcode及return-into-libc攻击
  7. PHP 获取服务器详细信息
  8. python自定义assert抛出的异常
  9. 用C#实现MD5算法
  10. Linux 系统启动
  11. 实用的两个网页小技巧(复制文本、下载文档)
  12. 面试必问JavaScript基础面试题(附答案详解)
  13. python控制电机正反转_连接电动机正反转控制电路,要求具备电气互锁功能,拍照上传...
  14. Python超市进销存管理系统!老妈开超市有系统了!
  15. 百度小程序SEO指南
  16. 谷歌浏览器(Chrome)输入框总是有历史输入记录,解决办法
  17. iCloud照片在哪看?如何查看iCloud里的照片
  18. 英语老师唱歌软件测试,【出彩教育人】课上打电话,课下能K歌,这样的英语课给我来一打!...
  19. mysql数据库有merge into 吗
  20. scrapy-redis中的指纹,去重的方式

热门文章

  1. linux增量安装tomcat_linux与windows下tomcat的java内存设置
  2. 航天金税 接口_用友凭证接口可以实现数据之间无缝对接和打通
  3. python3.5安装pygame_python怎么安装pygame
  4. java web 线程数_Java Web应用调优线程池
  5. date oracle 显示毫秒_Oracle date timestamp 毫秒 - 时间函数总结
  6. php date( ymd_PHP-date(),time()函数的应用
  7. 《剑指offer》第四十三题(从1到n整数中1出现的次数)
  8. P1101 单词方阵(DFS)
  9. 各种排序笔记---基于比较排序部分
  10. SQL递归查询(with as)