React虚拟DOM具体实现——利用节点json描述还原dom结构

react

  前两天,帮朋友解决一个问题:

   ajax请求得到的数据,是一个对象数组,每个对象中,具有三个属性,parentId,id,name,然后根据这个数据生成对应的结构。

  刚好最近在看React,并且了解到其中的虚拟DOM,其实,就是利用json数据来代替DOM结构表示,然后利用这个json数据,渲染出DOM树,总体添加到页面中。下面,我就通过介绍我如何实现上面实际问题的思路,一边完成实际需求,一边实现React中虚拟DOM渲染成DOM的原理。

  模拟数据结构如下:

 1 var allJson = [{

2 'id': '1',

3 'name': '我是1 我是根节点..我的长度是..'

4 }, {

5 'id': '2',

6 'parentId': '1',

7 'name': '我是2 我的父级是1..我的长度是..'

8 }, {

9 'id': '3',

10 'parentId': '2',

11 'name': '我是3 我的父级是2...我的长度是..'

12 }, {

13 'id': '8',

14 'parentId': '4',

15 'name': '我是8 我的父级是4..我的长度是..'

16 }, {

17 'id': '4',

18 'parentId': '2',

19 'name': '我是4 我的父级是2..我的长度是..'

20 }, {

21 'id': '5',

22 'parentId': '3',

23 'name': '我是5 我的父级是3..我的长度是..'

24 }, {

25 'id': '6',

26 'parentId': '1',

27 'name': '我是6 我的父级是1..我的长度是..'

28 }, {

29 'id': '7',

30 'parentId': '4',

31 'name': '我是7 我的父级是4..我的长度是..'

32 }];

方法一:直接将数据添加到页面中

  1)创建一个数组childRoot,用于存放已经添加到DOM树中的对象。(其中每一个元素,都可能有子节点),创建数组cloneAllJson,用于存放原始数据复制。

  2)利用根节点没有parentId,便利所有节点,找到根节点,并将该节点对象从cloneAllJson数组对象中移除(减少重复遍历)。

  3)循环childRoot数组,在数组剩下的节点对象中,根据parentId找到childRoot数组中的当前第一个元素的所有的子节点,每找到一个,直接添加到DOM树中,并添加到childRoot数组中,且从cloneAllJson数组中移除找到的子节点。添加完所有子节点后,移除childRoot的第一个元素。如此循环,直到childRoot数组长度为零。

代码如下:

 1 // 模拟数据

2 var allJson = [{

3 'id': '1',

4 'name': '我是1 我是根节点..我的长度是..'

5 }, {

6 'id': '2',

7 'parentId': '1',

8 'name': '我是2 我的父级是1..我的长度是..'

9 }, {

10 'id': '3',

11 'parentId': '2',

12 'name': '我是3 我的父级是2...我的长度是..'

13 }, {

14 'id': '8',

15 'parentId': '4',

16 'name': '我是8 我的父级是4..我的长度是..'

17 }, {

18 'id': '4',

19 'parentId': '2',

20 'name': '我是4 我的父级是2..我的长度是..'

21 }, {

22 'id': '5',

23 'parentId': '3',

24 'name': '我是5 我的父级是3..我的长度是..'

25 }, {

26 'id': '6',

27 'parentId': '1',

28 'name': '我是6 我的父级是1..我的长度是..'

29 }, {

30 'id': '7',

31 'parentId': '4',

32 'name': '我是7 我的父级是4..我的长度是..'

33 }, ];

34

35 /* 复制数据 */

36 var cloneAllJson = allJson.concat();

37

38 /* 找到根节点ID 并画出根节点到页面 */

39 /* 定义一个数组用来装每次新生成的次级父节点 */

40 var childRoot = [];

41 /* 遍历所有的接节点,查找根节点 */

42 for (var i = 0; i < allJson.length; i++) {

43 /* 如果不存在父节点字段 则为根节点 */

44 if (allJson[i].parentId == undefined) {

45 /* 赋值根节点ID */

46 rootId = allJson[i].id;

47 /* 将根节点添加到childRoot数组中,然后在复制的数组中删除这个根节点 */

48 childRoot.push(allJson[i]);

49 cloneAllJson.splice(i, 1);

50 /* 画出根节点 */

51 var div = document.createElement('div');

52 div.id = allJson[i].id;

53 div.appendChild(document.createTextNode(allJson[i].name));

54 document.getElementById('rootId').appendChild(div);

55 }

56 }

57

58

59 /*方法一:每次找到父节点的所有子节点,添加到dom树上,

60 * 并添加到childRoot数组中,从clone数组中剔除,(减少重复遍历)

61 * 直到childRoot数组中长度为0

62 * 可以解决,json数组中,父节点在子节点后面的问题

63 */

64 while (childRoot.length) {

65 /* 遍历cloneAllJson数组,找到childRoot第一个元素的所有子节点,并直接添加到页面上*/

66 for (var i = 0; i < cloneAllJson.length; i++) {

67 if (cloneAllJson[i].parentId == childRoot[0].id) {

68 /* 画出一级子节点 */

69 var div = document.createElement('div');

70 div.id = cloneAllJson[i].id;

71 div.appendChild(document.createTextNode(cloneAllJson[i].name));

72 /* 直接添加到页面上 */

73 document.getElementById(childRoot[0].id).appendChild(div);

74 /* 将该节点添加到childRoot中,之后遍历添加其子节点 */

75 childRoot.push(cloneAllJson[i]);

76 /* 将该节点从cloneAllJson数组中删除,并将索引向后减1 */

77 cloneAllJson.splice(i, 1);

78 i--;

79 }

80 }

81 /* 从childRoot数组中移除第一个元素(已经将其所有孩子添加到页面中) */

82 childRoot.shift();

83 }

最终生成dom结构如下图显示:

  其中rootId,是我们自己添加外节点。

  其实,在这个过程中,我强调了,每次找到节点,直接添加到页面上,在添加之前,都是先根据id查找父节点,其实,DOM操作性能很差,一般都是尽量减少DOM操作。在这里,我们就可以利用React中虚拟DOM渲染到页面上的方法了。

方法二:使用React虚拟DOM渲染方法

  其实主要思想还是方法一的思想,唯一不同,就是我们不是直接把节点对象添加到页面结构中,而是,给其父节点添加一个childObjs属性(用于存放所有子节点对象的数组)中。然后再利用递归,将所有节点渲染到页面上。其中,我们只进行了一次DOM查找操作,即最终调用render函数时。

代码如下:

  1 /* 模拟数据 */

2 var allJson = [{

3 'id': '1',

4 'name': '我是1 我是根节点..我的长度是..'

5 }, {

6 'id': '2',

7 'parentId': '1',

8 'name': '我是2 我的父级是1..我的长度是..'

9 }, {

10 'id': '3',

11 'parentId': '2',

12 'name': '我是3 我的父级是2...我的长度是..'

13 }, {

14 'id': '8',

15 'parentId': '4',

16 'name': '我是8 我的父级是4..我的长度是..'

17 }, {

18 'id': '4',

19 'parentId': '2',

20 'name': '我是4 我的父级是2..我的长度是..'

21 }, {

22 'id': '5',

23 'parentId': '3',

24 'name': '我是5 我的父级是3..我的长度是..'

25 }, {

26 'id': '6',

27 'parentId': '1',

28 'name': '我是6 我的父级是1..我的长度是..'

29 }, {

30 'id': '7',

31 'parentId': '4',

32 'name': '我是7 我的父级是4..我的长度是..'

33 }];

34

35 /* 数据复制 */

36 var cloneAllJson = allJson.concat();

37

38 /* 根节点对象 */

39 var root = {};

40 /* 定义一个数组用来装每次新生成的父节点 */

41 var childRoot = [];

42

43 /* 查找根节点,并记录根节点,并将该节点从数组中剔除 */

44 cloneAllJson.forEach(function(node, index) {

45 /* 不存在parentId,就是根节点root */

46 if (!node.parentId) {

47 /* 引入深度,方便后期控制样式 */

48 node.deep = 1;

49 root = node;

50 childRoot.push(root);

51 cloneAllJson.splice(index, 1);

52 }

53 });

54

55 /* 给所有childRoot节点中childObj中添加其子节点 */

56 while (childRoot.length) {

57 let parent = childRoot[0];

58 for (let j = 0; j < cloneAllJson.length; ++j) {

59 let node = cloneAllJson[j];

60 if (node.parentId == parent.id) {

61 node.deep = parent.deep + 1;

62 /* 引入childObjs,用于存放所有子节点对象的数组 */

63 if (!parent.childObjs) {

64 parent.childObjs = [];

65

66 }

67 parent.childObjs.push(node);

68 childRoot.push(node);

69 cloneAllJson.splice(j--, 1);

70 }

71 }

72 childRoot.shift();

73 }

74

75 console.log(root);

76

77 /* 渲染函数 */

78 function render(node, root) {

79 var elem;

80 /* 如果节点存在子节点对象,创建该节点,并递归调用渲染函数,将其渲染为该节点的子元素 */

81 /* 否则:直接渲染该节点*/

82 if (node.childObjs) {

83 var elem = createNode(node);

84 node.childObjs.forEach(function(item) {

85 render(item, elem);

86 });

87 } else {

88 var elem = createNode(node);

89 }

90 /* 添加到页面中的节点上 */

91 root.appendChild(elem);

92 })

93

94 // 创建节点工厂函数

95 function createNode(node) {

96 var div = document.createElement('div');

97 div.style.paddingLeft = 20 + 'px';

98 div.style.fontSize = 16 - node.deep + 'px';

99 div.appendChild(document.createTextNode(node.name));

100 return div;

101 }

最终显示结果截图:

  其实准确的说,我一共写了四种实现方法,但是这两种,是其中最好简单的两种,希望大家批评指正。

  

github上函数地址:https://github.com/DiligentYe/my-frame/blob/master/json-to-dom.js

以上是 React虚拟DOM具体实现——利用节点json描述还原dom结构 的全部内容, 来源链接: utcz.com/z/383964.html

回到顶部