React虚拟DOM具体实现——利用节点json描述还原dom结构
前两天,帮朋友解决一个问题:
ajax请求得到的数据,是一个对象数组,每个对象中,具有三个属性,parentId,id,name,然后根据这个数据生成对应的结构。
刚好最近在看React,并且了解到其中的虚拟DOM,其实,就是利用json数据来代替DOM结构表示,然后利用这个json数据,渲染出DOM树,总体添加到页面中。下面,我就通过介绍我如何实现上面实际问题的思路,一边完成实际需求,一边实现React中虚拟DOM渲染成DOM的原理。
模拟数据结构如下:
var allJson = [{
'id': '1',
'name': '我是1 我是根节点..我的长度是..'
}, {
'id': '2',
'parentId': '1',
'name': '我是2 我的父级是1..我的长度是..'
}, {
'id': '3',
'parentId': '2',
'name': '我是3 我的父级是2...我的长度是..'
}, {
'id': '8',
'parentId': '4',
'name': '我是8 我的父级是4..我的长度是..'
}, {
'id': '4',
'parentId': '2',
'name': '我是4 我的父级是2..我的长度是..'
}, {
'id': '5',
'parentId': '3',
'name': '我是5 我的父级是3..我的长度是..'
}, {
'id': '6',
'parentId': '1',
'name': '我是6 我的父级是1..我的长度是..'
}, {
'id': '7',
'parentId': '4',
'name': '我是7 我的父级是4..我的长度是..'
}];
方法一:直接将数据添加到页面中
1)创建一个数组childRoot,用于存放已经添加到DOM树中的对象。(其中每一个元素,都可能有子节点),创建数组cloneAllJson,用于存放原始数据复制。
2)利用根节点没有parentId,便利所有节点,找到根节点,并将该节点对象从cloneAllJson数组对象中移除(减少重复遍历)。
3)循环childRoot数组,在数组剩下的节点对象中,根据parentId找到childRoot数组中的当前第一个元素的所有的子节点,每找到一个,直接添加到DOM树中,并添加到childRoot数组中,且从cloneAllJson数组中移除找到的子节点。添加完所有子节点后,移除childRoot的第一个元素。如此循环,直到childRoot数组长度为零。
代码如下:
// 模拟数据
var allJson = [{
'id': '1',
'name': '我是1 我是根节点..我的长度是..'
}, {
'id': '2',
'parentId': '1',
'name': '我是2 我的父级是1..我的长度是..'
}, {
'id': '3',
'parentId': '2',
'name': '我是3 我的父级是2...我的长度是..'
}, {
'id': '8',
'parentId': '4',
'name': '我是8 我的父级是4..我的长度是..'
}, {
'id': '4',
'parentId': '2',
'name': '我是4 我的父级是2..我的长度是..'
}, {
'id': '5',
'parentId': '3',
'name': '我是5 我的父级是3..我的长度是..'
}, {
'id': '6',
'parentId': '1',
'name': '我是6 我的父级是1..我的长度是..'
}, {
'id': '7',
'parentId': '4',
'name': '我是7 我的父级是4..我的长度是..'
}, ]; /* 复制数据 */
var cloneAllJson = allJson.concat(); /* 找到根节点ID 并画出根节点到页面 */
/* 定义一个数组用来装每次新生成的次级父节点 */
var childRoot = [];
/* 遍历所有的接节点,查找根节点 */
for (var i = 0; i < allJson.length; i++) {
/* 如果不存在父节点字段 则为根节点 */
if (allJson[i].parentId == undefined) {
/* 赋值根节点ID */
rootId = allJson[i].id;
/* 将根节点添加到childRoot数组中,然后在复制的数组中删除这个根节点 */
childRoot.push(allJson[i]);
cloneAllJson.splice(i, 1);
/* 画出根节点 */
var div = document.createElement('div');
div.id = allJson[i].id;
div.appendChild(document.createTextNode(allJson[i].name));
document.getElementById('rootId').appendChild(div);
}
} /*方法一:每次找到父节点的所有子节点,添加到dom树上,
* 并添加到childRoot数组中,从clone数组中剔除,(减少重复遍历)
* 直到childRoot数组中长度为0
* 可以解决,json数组中,父节点在子节点后面的问题
*/
while (childRoot.length) {
/* 遍历cloneAllJson数组,找到childRoot第一个元素的所有子节点,并直接添加到页面上*/
for (var i = 0; i < cloneAllJson.length; i++) {
if (cloneAllJson[i].parentId == childRoot[0].id) {
/* 画出一级子节点 */
var div = document.createElement('div');
div.id = cloneAllJson[i].id;
div.appendChild(document.createTextNode(cloneAllJson[i].name));
/* 直接添加到页面上 */
document.getElementById(childRoot[0].id).appendChild(div);
/* 将该节点添加到childRoot中,之后遍历添加其子节点 */
childRoot.push(cloneAllJson[i]);
/* 将该节点从cloneAllJson数组中删除,并将索引向后减1 */
cloneAllJson.splice(i, 1);
i--;
}
}
/* 从childRoot数组中移除第一个元素(已经将其所有孩子添加到页面中) */
childRoot.shift();
}
最终生成dom结构如下图显示:

其中rootId,是我们自己添加外节点。
其实,在这个过程中,我强调了,每次找到节点,直接添加到页面上,在添加之前,都是先根据id查找父节点,其实,DOM操作性能很差,一般都是尽量减少DOM操作。在这里,我们就可以利用React中虚拟DOM渲染到页面上的方法了。
方法二:使用React虚拟DOM渲染方法
其实主要思想还是方法一的思想,唯一不同,就是我们不是直接把节点对象添加到页面结构中,而是,给其父节点添加一个childObjs属性(用于存放所有子节点对象的数组)中。然后再利用递归,将所有节点渲染到页面上。其中,我们只进行了一次DOM查找操作,即最终调用render函数时。
代码如下:
/* 模拟数据 */
var allJson = [{
'id': '1',
'name': '我是1 我是根节点..我的长度是..'
}, {
'id': '2',
'parentId': '1',
'name': '我是2 我的父级是1..我的长度是..'
}, {
'id': '3',
'parentId': '2',
'name': '我是3 我的父级是2...我的长度是..'
}, {
'id': '8',
'parentId': '4',
'name': '我是8 我的父级是4..我的长度是..'
}, {
'id': '4',
'parentId': '2',
'name': '我是4 我的父级是2..我的长度是..'
}, {
'id': '5',
'parentId': '3',
'name': '我是5 我的父级是3..我的长度是..'
}, {
'id': '6',
'parentId': '1',
'name': '我是6 我的父级是1..我的长度是..'
}, {
'id': '7',
'parentId': '4',
'name': '我是7 我的父级是4..我的长度是..'
}]; /* 数据复制 */
var cloneAllJson = allJson.concat(); /* 根节点对象 */
var root = {};
/* 定义一个数组用来装每次新生成的父节点 */
var childRoot = []; /* 查找根节点,并记录根节点,并将该节点从数组中剔除 */
cloneAllJson.forEach(function(node, index) {
/* 不存在parentId,就是根节点root */
if (!node.parentId) {
/* 引入深度,方便后期控制样式 */
node.deep = 1;
root = node;
childRoot.push(root);
cloneAllJson.splice(index, 1);
}
}); /* 给所有childRoot节点中childObj中添加其子节点 */
while (childRoot.length) {
let parent = childRoot[0];
for (let j = 0; j < cloneAllJson.length; ++j) {
let node = cloneAllJson[j];
if (node.parentId == parent.id) {
node.deep = parent.deep + 1;
/* 引入childObjs,用于存放所有子节点对象的数组 */
if (!parent.childObjs) {
parent.childObjs = []; }
parent.childObjs.push(node);
childRoot.push(node);
cloneAllJson.splice(j--, 1);
}
}
childRoot.shift();
} console.log(root); /* 渲染函数 */
function render(node, root) {
var elem;
/* 如果节点存在子节点对象,创建该节点,并递归调用渲染函数,将其渲染为该节点的子元素 */
/* 否则:直接渲染该节点*/
if (node.childObjs) {
var elem = createNode(node);
node.childObjs.forEach(function(item) {
render(item, elem);
});
} else {
var elem = createNode(node);
}
/* 添加到页面中的节点上 */
root.appendChild(elem);
}) // 创建节点工厂函数
function createNode(node) {
var div = document.createElement('div');
div.style.paddingLeft = 20 + 'px';
div.style.fontSize = 16 - node.deep + 'px';
div.appendChild(document.createTextNode(node.name));
return div;
}
最终显示结果截图:

其实准确的说,我一共写了四种实现方法,但是这两种,是其中最好简单的两种,希望大家批评指正。
github上函数地址:https://github.com/DiligentYe/my-frame/blob/master/json-to-dom.js
React虚拟DOM具体实现——利用节点json描述还原dom结构的更多相关文章
- JSON还原为结构体
JSON还原为结构体 1)JSON字符串还原为结构体: 2)访问结构体的字段值: 本例运行效果图: uses SynCommons; const // JSON字符串 JSON1 = '{' + #1 ...
- React虚拟DOM浅析
在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,但是复杂或频繁的DOM操作通常是性能瓶颈产生的原因,为此,React引入了虚拟DOM(Virtual DOM)的机制. 什 ...
- React 虚拟 DOM 的差异检测机制
React 使用虚拟 DOM 将计算好之后的更新发送到真实的 DOM 树上,减少了频繁操作真实 DOM 的时间消耗,但将成本转移到了 JavaScript 中,因为要计算新旧 DOM 树的差异嘛.所以 ...
- react虚拟dom diff算法
react虚拟dom:依据diff算法 前端:更新状态.更新视图:所以前端页面的性能问题主要是由Dom操作引起的,解放Dom操作复杂性 刻不容缓 因为:Dom渲染慢,而JS解析编译相对非常非常非常快! ...
- react系列一,react虚拟dom如何转成真实的dom
react,想必作为前端开发一定不陌生,组件化以及虚拟dom使得react成为最受欢迎额前端框架之一.我们知道react是基于虚拟dom的,但是什么是虚拟dom呢,其实就是一组js对象,那么我们今天就 ...
- 深入理解React虚拟DOM
一.什么是虚拟DOM 虚拟DOM可以看做一棵模拟了DOM树的JavaScript对象树.比如: var element = { element: 'ul', props: { id:"uli ...
- React/虚拟DOM
在说虚拟DOM之前,先来一个引子,从输入url到展现出整个页面都有哪些过程? 1.输入网址 2.DNS解析 3.建立tcp连接 4.客户端发送HTPP请求 5.服务器处理请求 6.服务器响应请求 7. ...
- JQUERY选择和操作DOM元素(利用正则表达式的方法匹配字符串中的一部分)
JQUERY选择和操作DOM元素(利用正则表达式的方法匹配字符串中的一部分) 1.匹配属性的开头 $("[attributeName^='value']"); 2.匹配属性的结尾 ...
- Javascript高级编程学习笔记(35)—— DOM(1)节点
DOM JS由三部分组成 1.BOM 2.DOM 3.ECMAScript ES和BOM在前面的文章已经介绍过了 今天开始JS组成的最后一部分DOM(文档对象模型) 我们知道,JS中的这三个部分实际上 ...
随机推荐
- JUnit与JMock学习
JUnit与JMock学习 测试驱动编程和持续集成部署应该说是现在软件开发者的必备武器,不过跟其他很多好东西一样,在我们公司的推广总要慢上一拍,毕竟老板看的是你能够把功能实现好让客户满意,所以能不折腾 ...
- MAC上配置asp.net core开发环境
安装.NET Core sdk https://www.microsoft.com/net/core#macos 安装VS Code https://code.visualstudio.com/Dow ...
- VMware WorkStation9.0虚拟机如何运行WINPE
VMware WorkStation9.0虚拟机如何运行WINPE...我们在使用VMware的过程中,有些时候需要让VMware来运行WINPE系统,比如在使用VMware进行系统封装的过程中,其中 ...
- Tinyshell: 一个简易的shell命令解释器
这是自己最近学习Linux系统编程之后写的一个练手的小程序,能很好地复习系统编程中的进程管理.信号.管道.文件等内容. 通过回顾写的过程中遇到的问题的形式记录程序的关键点,最后给出完整程序代码. 0. ...
- 从 http 升级到 https 过程中遇到的一些问题
Apple 2017年1月1号起要求Appstore 上线的应用都必须使用 https 加密请求协议,在二月份又改为建议 从 http 升级为 https 协议,此为背景. 公司做的APP同时在App ...
- 第27篇 重复造轮子---模拟IIS服务器
在写程序的时候,重复造轮子是程序员的一个大忌,很多人对重复造轮子持有反对的态度,但是我觉得这个造轮子的过程,是对于现有的知识的一个深入的探索的过程,虽然我们不可能把轮子造的那么的完善,对于现在有的东西 ...
- 微信小程序怎样提高应用速度小技巧
作者:vicyao, 腾讯web前端开发 高级工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/294.htm ...
- observe.js 源码 学习笔记
/** * observejs --- By dnt http://kmdjs.github.io/ * Github: https://github.com/kmdjs/observejs * MI ...
- iOS 获取手机型号,系统版本
新添加判断iPhone 7.iPhone 7 Plus ,我手里没有7,判断不对表打我~ FQ找的资料:http://www.iphonehacks.com/download-iphone-ios-f ...
- ASP.NET脚本过滤-防止跨站脚本攻击(收集别人的)
ASP.Net 1.1后引入了对提交表单自动检查是否存在XSS(跨站脚本攻击)的能力.当用户试图用<xxxx>之类的输入影响页面返回结果的时候,ASP.Net的引擎会引发一个 HttpRe ...