虚拟DOM

# 虚拟 DOM

本章分析了虚拟 DOM 的使用场景、常用 API、以及 diff 算法 的代码框架。通过学习和了解虚拟 DOM ,为后面的 vue 和 React 学习打好基础。

知识点

5-1 什么是 vdom,为何使用 vdom?

5-2 vdom 的如何应用,核心 API 是什么?

5-3 介绍 一下 diff 算法

Ignorance is the curse of God, knowledge the wing wherewith we fly to heaven.——William Shakespeare

无知乃是罪恶,知识乃是我们借以飞向天堂的翅膀。—— 莎士比亚

# virtual dom

  • vdom 是 vue 和 React 的核心,先讲哪个都绕不开它
  • vdom 比较独立,使用也比较简单
  • 如果面试问到 vue 和 React 的实现,免不了问 vdom

# 问题

  • vdom 是什么?为何会存在 vdom?
  • vdom 的如何应用,核心 API 是什么?
  • 介绍一下 diff 算法

# 5-1 什么是 vdom,为何使用 vdom?

# 知识点

  • 什么是 vdom?
  • 设计一个需求场景
  • 用 jQuery 实现
  • 遇到的问题

# 什么是 vdom?

  • virtual dom,虚拟 DOM
  • 用 JS 模拟 DOM 结构
  • DOM 变化的对比,放在 JS 层来做 ( 图灵完备语言 )
  • 提高重绘性能
<ul id="list">
  <li class="item">Item 1</li>
  <li class="item">Item 2</li>
</ul>

用 JS 模拟 DOM

// vdom
{
    tag:'ul',
    attrs:{
        id:'list'
    },
    children:[
        {
            tag:'li',
            attrs:{className:'item'},
            children:['Item 1']
        },
        {
            tag:'li',
            attrs:{className:'item'},
            children:['Item 2']
        }
    ]
}

# 设计一个需求场景

/*
 * 1.将该数据展示成一个表格
 * 2.随便修改一个信息,表格也跟着修改
 * */
[
  {
    name: "张三",
    age: "20",
    address: "北京",
  },
  {
    name: "李四",
    age: "21",
    address: "上海",
  },
  {
    name: "王五",
    age: "22",
    address: "广州",
  },
];

jQuery 实现

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="container"></div>
    <button id="btn-change">change</button>

    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
      var data = [
        {
          name: "张三",
          age: "20",
          address: "北京",
        },
        {
          name: "李四",
          age: "21",
          address: "上海",
        },
        {
          name: "王五",
          age: "22",
          address: "广州",
        },
      ];

      //渲染函数
      function render(data) {
        var $container = $("#container");
        //清空容器
        $container.html("");

        //拼接  table
        var $table = $("<table>");
        $table.append($("<tr><td>name</td><td>age</td><td>address</td></tr>"));
        data.forEach(function (item) {
          $table.append(
            $(
              "<tr><td>" +
                item.name +
                "</td><td>" +
                item.age +
                "</td><td>" +
                item.address +
                "</td></tr>"
            )
          );
        });

        //渲染 到页面   jQuery放在这里:只有 一次DOM渲染,性能更好,但是 并不符合理想情况
        $container.append($table);
      }

      $("#btn-change").click(function () {
        data[1].age = 30;
        data[2].address = "深圳";
        //  re-render 再次渲染
        render(data);
      });

      //页面加载完,立即执行
      render(data);
    </script>
  </body>
</html>

mark

# 遇到的问题

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

# 问题解答

  • vdom 是什么?为何会存在 vdom?

    • virtual dom,虚拟 DOM
    • 用 JS 模拟 DOM 结构
    • DOM 操作非常 “昂贵”
    • 将 DOM 对比操作放在 JS 层,提高效率
  • vdom 的如何应用,核心 API 是什么?

    • 介绍 snabbdom
    • 重做 之前的 demo
    • 核心 API

# 5-2 vdom 的如何应用,核心 API 是什么?

介绍 snabbdom :一个 vdom 实现库。

# 介绍 snabbdom

虚拟 DOM 非常棒。它允许我们将应用程序的视图表示为其状态的函数。但现有的解决方案太过臃肿,太慢,缺乏功能,API 偏向于 OOP 和 / 或缺少我需要的功能。

Snabbdom 由一个非常简单,高性能和可扩展的核心组成,只有 ≈200SLOC。它提供了模块化架构,具有丰富的功能,可通过自定义模块进 为了保持核心简单,所有非必要功能都委托给模块。

你可以将 Snabbdom 塑造成你想要的任何东西!选择,选择和自定义所需的功能。或者,您可以使用默认扩展并获得具有高性能,小尺寸和下面列出的所有功能的虚拟 DOM 库。

# 介绍 snabbdom - h 函数

mark

# 介绍 snabbdom -patch 函数

mark

# 使用 snabbdom

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="container"></div>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.min.js"></script>
    <script>
      var snabbdom = window.snabbdom;

      //定义 patch
      var patch = snabbdom.init([
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners,
      ]);

      // 定义 h
      var h = snabbdom.h;

      var container = document.getElementById("container");

      //生成 vnode
      var vnode = h("ul#list", {}, [
        h("li.item", {}, "Item 1"),
        h("li.item", {}, "Item 2"),
      ]);

      patch(container, vnode);

      document
        .getElementById("btn-change")
        .addEventListener("click", function () {
          //生成 newVnode
          var newVnode = h("ul#list", {}, [
            h("li.item", {}, "Item 1"),
            h("li.item", {}, "Item B"),
            h("li.item", {}, "Item 3"),
          ]);
          patch(vnode, newVnode);
        });
    </script>
  </body>
</html>

mark

# 重做 之前的 demo

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="container"></div>
    <button id="btn-change">change</button>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.min.js"></script>
    <script src="https://cdn.bootcss.com/snabbdom/0.7.3/h.min.js"></script>
    <script>
      var snabbdom = window.snabbdom;

      //定义 patch
      var patch = snabbdom.init([
        snabbdom_class,
        snabbdom_props,
        snabbdom_style,
        snabbdom_eventlisteners,
      ]);

      // 定义 h
      var h = snabbdom.h;

      //原始数据
      var data = [
        {
          name: "张三",
          age: "20",
          address: "北京",
        },
        {
          name: "李四",
          age: "21",
          address: "上海",
        },
        {
          name: "王五",
          age: "22",
          address: "广州",
        },
      ];

      //把表也放在 data 中
      data.unshift({
        name: "姓名",
        age: "年龄",
        address: "地址",
      });

      var container = document.getElementById("container");

      //渲染函数
      var vnode;

      function render(data) {
        var newVnode = h(
          "table",
          {},
          data.map(function (item) {
            var tds = [];
            var i;
            for (i in item) {
              if (item.hasOwnProperty(i)) {
                tds.push(h("td", {}, item[i] + ""));
              }
            }
            return h("tr", {}, tds);
          })
        );

        if (vnode) {
          // 再次渲染
          patch(vnode, newVnode);
        } else {
          // 初次渲染
          patch(container, newVnode);
        }
        vnode = newVnode;
      }

      // 初次渲染
      render(data);

      var btnChange = document.getElementById("btn-change");
      btnChange.addEventListener("click", function () {
        data[1].age = 30;
        data[2].address = "深圳";
        //re-render
        render(data);
      });
    </script>
  </body>
</html>

mark

# 核心 API

  • h(’<标签名>’,{ … 属性 … },[… 子元素 …])
  • h(’<标签名>’,{ … 属性 … },[ ‘…’])
  • patch(container,vnode)
  • patch(vnode,newVnode)

# 问题解答

  • vdom 的如何应用,核心 API 是什么?
    • 如何使用? 可用 snabbdom 的 用法 来 举例
    • 核心 函数 :h 函数,patch 函数

# 5-3 介绍 一下 diff 算法

# 题目

  • 什么是 diff 算法?
  • 去繁就简
  • vdom 为何用 diff 算法 ?
  • diff 算法 的 实现流程

# 什么是 diff 算法

diff 算法 一直在我们身边

并不是 Vue 和 React 创造出来的概念

diff 算法命令演示

  • linux 系统下: diff log1.txt log2.txt :比较 2 个文件的不同
  • git diff xxxx 的示例 : git 里面比较不同版本间的代码差异

# 去繁就简

  • diff 算法非常复杂,实现难度很大,源码量很大
  • 去繁就简,讲明白核心流程 ,不关心细节
  • 面试官也大部分都不清楚细节,但是很关心核心流程
  • 去繁就简之后,依然 具有很大挑战性,并不简单

# vdom 为何用 diff 算法

  • DOM 操作是 “昂贵” 的,因此尽量减少 DOM 操作
  • 找出本次 DOM 必须更新的节点来更新,其它的 不更新
  • 这个 “找出” 的过程,就需要 diff 算法

# diff 实现过程

  • patch (container,vnode)
  • patch (vnode,newVnode)

# patch (container,vnode)

mark

核心:如何使用 左边的 JS 节点 生成 右侧 Dom 节点?

mark

# patch (vnode,newVnode)

核心:** 对比 **

mark

mark

mark

# diff 实现过程

  • patch (container,vnode)和 patch (vnode,newVnode)
  • createElment
  • updataChildren

# 问题解答

  • 介绍一下 diff 算法?
    • 知道什么是 diff 算法,是 linux 的基础命令
    • vdom 中 应用 diff 算法目的: 是为了 找出需要更新的节点
    • diff 实现:patch (container,vnode)和 patch (vnode,newVnode)
    • 核心 逻辑 , createElment 和 updataChildren
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2015-2021 zhou chen
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信