乐趣区

关于javascript:虚拟DOM如何进化为真实DOM

前言

VueReactRender函数中都波及到了 Virtual DOM 的概念,Virtual DOM也是性能优化上的重要一环,同时冲破了间接操作 实在 DOM的瓶颈,本文带着以下几个问题来论述Virtual DOM

  • 1. 为什么要操作虚构 DOM?
  • 2. 什么是虚构 DOM?
  • 3. 手把手教你实现虚构 DOM 渲染实在 DOM

心愿浏览本文之后,可能让你深刻的理解 虚构 DOM并且在开发和面试中收益。

为什么要操作虚构 DOM

为了帮忙咱们更好的了解为什么要操作虚构 DOM,咱们先从浏览器渲染一个 HTML 文件须要做哪些事件说起:

浏览器渲染机制大抵能够分为以下 5 步走:

  • 1. 创立 DOM tree
  • 2. 创立 Style Rules
  • 3. 构建 Render tree
  • 4. 布局 Layout
  • 5. 绘制 Painting

咱们过来应用 原生 JavaScriptjquery 去操作 实在 DOM的时候, 浏览器会从构建 DOM???? 开始从头到尾的执行一遍渲染的流程。

在一次开发中, 如果产品通知你一个需要, 你须要在一次操作中更新 10 个 DOM 节点, 现实状态是浏览器 一次性构建完 DOM 树,再执行后续操作。但浏览器没这么智能,收到第一个更新 DOM 申请后,并不知道后续还有 9 次更新操作,因而会马上执行流程,最终执行 10 次流程。

过了一会产品经理把你叫过来和你说把需要改一下, 此时你又须要操作一次 DOM 的更新,那么这个时候之前做的 10 次 DOM 操作就是白白 节约性能, 节约感情。

即便计算机硬件始终在更新迭代,然而 操作 DOM的代价仍旧是低廉的,频繁操作 DOM 还是会呈现 页面卡顿,影响用户的体验。实在的 DOM 节点,哪怕一个最简略的 div 也蕴含着很多属性,能够打印进去直观感受一下:

如此多的属性,如果每次对 DOM 构造都进行更新,一次,两次,三次 … 一百次 …. 一千次 …,可想而知,是如许宏大的数据量。

因而 虚构 DOM就是为了解决这个浏览器性能问题而被设计进去的。例如后面的例子,如果一次操作中有 10 次更新 DOM 的动作,虚构 DOM 不会立刻操作 DOM,而是将这 10 次更新 DOM 的动作通过 Diff 算法 最终生成 一个 js 对象 ,而后告诉浏览器去执行一次绘制工作,这样能够防止 大量的无谓的计算量

什么是虚构 DOM

虚构 DOM[2]就是咱们下面所说的js 对象

其本质上就是在 JSDOM之间做了一个缓存。能够类比 CPU 硬盘 ,既然 硬盘 这么慢,咱们就在它们之间加个 缓存 :既然 DOM 这么慢,咱们就在它们 JS 和 DOM 之间加个 缓存 CPU(JS) 只操作 内存(Virtual DOM),最初的时候再把变更写入硬盘(DOM),间接操作内存中的 JS 对象的速度显然要更快

function vnode(tag, data, key, children, text) {
    return {
        tag,
        data,
        key,
        children,
        text
    }
}

举个栗子:

如果咱们有这样的一个 DOM 树

<ul class="list">
  <li class="item"> 前端简报 </li>
  <li>vue</li>
</ul>

那么,咱们怎么用 js 的对象来对应到这个树呢?

{
    tag: 'ul',        // 元素标签
    data: {           // 属性
        class: 'list'
    },
    key: '',
    text: '',  // 文本内容
    children: [
        {
            tag: "li",
            data: {class: "item"},
            key: '',
            text: '',
            children: [
                {
                    tag: undefined,
                    data: undefined,
                    key: undefined,
                    text: '前端简报',
                    children: []}
            ]
        },
        {
            tag: "li",
            data: "",
            key: '',
            text: '',
            children: [
                {
                    tag: undefined,
                    data: undefined,
                    key: undefined,
                    text: 'vue',
                    children: []}
            ]
        }
    ]       // 子元素
}

由此可知:DOM tree的信息都能够用 JavaScript 对象 来示意,反过来,咱们也能够用 JavaScript 对象 示意的树结构来构建一棵 真正的 DOM 树

实现虚构 DOM 渲染实在 DOM

有了 JavaScript 对象 之后如何转化为实在的 DOM 树结构呢?

ul 和 li 在 js 对象中, 页面上并没有此构造,所以咱们须要把 ulli转化为 <ul><li>标签

而文本标签咱们定义 Vnode 为:

 {
    tag: undefined,
    data: undefined,
    key: undefined,
    text: 'vue',
    children: []}

故能够判断 tag 的类型来确定 创立元素 的类型.

function createElm(vnode) {let { tag, data, children, key, text} = vnode;
    if (typeof tag == "string") {vnode.el = document.createElement(tag);  // 创立元素放到 vnode.el 上
        children.forEach(child => {vnode.el.appendChild(createElm(child))
        })
    } else {vnode.el = document.createTextNode(text);  // 创立文本
    }
    return vnode.el
}

如果子节点存在并且也是 虚构 DOM的话,咱们通过 递归 调用创立子节点。

创立 DOM 树结构之后咱们须要设置节点的属性,即解决虚构 DOM 中的 data 属性。

function updateProperties(vnode) {
    let el = vnode.el;
    let newProps = vnode.data || {};
    for (let key in newProps) {if (key == "style") {for (let styleName in newProps.style) {el.style[styleName] = newProps.style[styleName];
            }
        } elseif (key == "class") {el.className = newProps.class;} else {el.setAttribute(key, newProps[key]);
        }
    }
}

在咱们 创立元素标签 之后调用 updateProperties 办法即可

把下面创立进去的实在 DOM 构造 vnode.el 增加到文档当中即可呈现出咱们须要的实在 DOM 构造

let parentElm = document.getElementById("app").parentNode; 获取之前 app 的父亲 body
parentElm.insertBefore(createElm(vnode), document.getElementById("app").nextSibling); //body 里在老的 app 前面插入实在 dom
parentElm.removeChild(document.getElementById("app")); // 删除老的节点

总结

以上就是本文的全部内容, 我想咱们当初应该理解什么是 虚构 DOM 的概念 了以及 虚构 DOM 是如何实现实在 DOM 渲染的 。其中用到了次要用到了 子节点的递归,下篇文章将解说_虚构节点的 diff 算法_,敬请期待。

参考资料

虚构 DOM 介绍: https://www.jianshu.com/p/616…

如何实现一个 Virtual DOM 算法: ‘https://github.com/livoras/bl…’

退出移动版