1.什么是虚拟DOM

  • 用JS模拟DOM结构
  • DOM变化的对比,放在JS层来做
  • 提高重绘性能

2.模拟DOM表示形式

  1. // 真实dom
  2. <ul id='list'>
  3. <li class='item'>Item 1</li>
  4. <li class='item'>Item 2</li>
  5. <ul>
  6. // 虚拟dom
  7. {
  8. tag:'ul',
  9. attrs:{
  10. id:'list'
  11. },
  12. children:[
  13. {
  14. tag:'li',
  15. attrs:{className:'item'},
  16. children:['Item1']
  17. },{
  18. tag:'li',
  19. attrs:{className:'item'}
  20. children:['Item 2']
  21. }
  22. ]
  23. }

3.创建DOM遇到的问题

我们来看一段代码

  1. <body>
  2. <ul id="wrap"></ul>
  3. <button id="btn">改变</button>
  4. </body>
  5. <script>
  6. const person = [
  7. {
  8. name: "xiao ming",
  9. age: 25,
  10. },
  11. {
  12. name: "xiao zhang",
  13. age: 23,
  14. },
  15. ];
  16. const btn = document.getElementById("btn");
  17. const wrap = document.getElementById("wrap");
  18. btn.addEventListener("click",function(){
  19. person[0].name = 'xiao li'
  20. render(person)
  21. })
  22. function render(params) {
  23. wrap.innerHTML = ''
  24. params.forEach((item) => {
  25. let cell = document.createElement("li");
  26. cell.innerHTML = `${item.name}-${item.age}`;
  27. wrap.appendChild(cell);
  28. });
  29. }
  30. render(person);
  31. </script>

当我们点击按钮时,我们改变了xiao ming的名称,但是dom帮我们把整个div的结构都重新渲染了一遍。如果我们在一个很大的工程中重复的做类似的操作,就会使我们的项目运行的很卡。所以就此引出了虚拟DOM的概念,我们只关心改变的那个DOM其他的不用去管。

我们来看一个创建div的操作。

  1. <script>
  2. var div = document.createElement('div')
  3. var item,result = ''
  4. for(item in div){
  5. result += ' | ' + item ;
  6. }
  7. console.log(result)
  8. </script>

image.png

从上图中可以看出,我们每一次创建DOM都会创建出这么多属性来,如果多次创建,可以想象浏览器的性能。

  • DOM操作是“昂贵”的,js运行的效率高
  • 尽量减少DOM操作,而不是“推到重来”
  • 项目越复杂,影响就越严重
  • vdom即可解决这个问题
  • 将DOM对比操作放在js层,提高效率

4.虚拟DOM案例

这里我们借助了一个虚拟dom的库snabbdom https://github.com/snabbdom/snabbdom

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>vdom</title>
  8. <style>
  9. li {
  10. list-style: none;
  11. }
  12. </style>
  13. </head>
  14. <body>
  15. <div id="container"></div>
  16. <button id="btn-change">change</button>
  17. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom.js"></script>
  18. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom-class.js"></script>
  19. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom-props.js"></script>
  20. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom-style.js"></script>
  21. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom-eventlisteners.js"></script>
  22. <script src="https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/h.js"></script>
  23. <script>
  24. var snabbdom = window.snabbdom;
  25. var patch = snabbdom.init([
  26. snabbdom_class,
  27. snabbdom_props,
  28. snabbdom_style,
  29. snabbdom_eventlisteners,
  30. ]);
  31. var h = snabbdom.h;
  32. var container = document.getElementById("container");
  33. var vnode = h("ul#list", {}, [
  34. h("li.item", {}, "Item 1"),
  35. h("li.item", {}, "Item 2"),
  36. ]);
  37. patch(container, vnode);
  38. const btn = document.getElementById("btn-change");
  39. btn.addEventListener("click", function () {
  40. var newvnode = h("ul#list", {}, [
  41. h("li.item", {}, "Item 1"),
  42. h("li.item", {}, "Item 3"),
  43. h("li.item", {}, "Item 4"),
  44. ]);
  45. patch(vnode, newvnode);
  46. });
  47. </script>
  48. </body>
  49. </html>

核心API:h函数、patch函数
打开浏览器运行一下案例,可以发现DOM不是整块重新渲染,它只是对修改的模块进行了重新渲染。

5.虚拟DOM和真实DOM更新数据操作对比

原始操作DOM:注销旧 DOM -> 数据 + 模板 => 新的一套HTML 代码 -> 挂载新 DOM
使用虚拟DOM:数据 + 模板 = 虚拟 DOM -> diff 新旧两套虚拟 DOM 的差异,得到补丁集 -> 把“补丁”打到真实 DOM 上

6.React中使用Key的作用

  • 组件的DOM结构是相对稳定
  • 类型相同的兄弟节点可以被唯一标识
  • 算法复杂度O(n)