>[success] # 简单的了解虚拟DOM 1. vue 无论是模板通语法将模板转换为`render`,或者直接使用`render`的形式也罢,其实都是为接下来转换**虚拟DOM**做准备 2. 由普通的 JS 对象来描述 DOM 对象,因为不是真实的 DOM 对象,所以叫 `Virtual DOM`(虚拟DOM),虚拟DOM 节点叫`VNode`(虚拟节点) >[danger] ##### 打印真实dom 1. 既然有了虚拟dom,那么真实dom是什么?把下面的代码在浏览器中运行 ~~~ let div = document.createElement('div') let str = '' for (const key in div) { str += key + '' } console.log(str) ~~~ 2. 上面代码运行后,可以发现输出了**很多的属性**,可以发现一个真正的 **DOM 元素是非常庞大**,这些对象属性并不是**实际开发过程都会用上**的如果有一个用js表现的dom的结构,再将这个结构渲染会成为dom可以让开发阶段更关心数据 >[info] ## 虚拟dom 1. 手动操作DOM时代 ,即使有了jq帮助我们对dom做了简化的操作,但随着项目的复杂DOM操作也变得复杂起来 2. 后来也出现了一种产物模板方式进行dom操作,就例如**art-template**,但依旧不能解决跟踪状态变化 3. 随着vue 这类数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM,而操作真实DOM又是**非常耗费性能** 4. 想跟踪变化,就需要进行比较,比较后知道哪里改变了,去改变对应位置思路就诞生了,**如果说维护两个dom**,每次两个dom进行比较,这原本一个dom就相对比较浪费,现在又用两个,这个肯定不行,如果我们用**js形式记录两个数组**,每次比较两个数组那个位置变化了,再去操作对应变化的局部的dom是不是会更好(两个dom 比较不好的主要原因通过打印dom 属性看到的数量上,这么多属性每次都要最对比是个开销,并且并不是每个属性都用了) 5. **Virtual DOM**(虚拟dom) 的好处是当状态改变时**不需要立即更新 DOM**,只需要创建一个虚拟树来描述DOM, Virtual DOM 内部将弄清楚如何 **有效(diff)** 的更新 DOM >[danger] ##### 虚拟DOM能带来的 1. 由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,不同的运行环境进行代码转换,所以使它具有了**跨平台的能力**,比如说**SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app)** 2. 优化性能 。DOM 操作是**比较耗时**的,对于**大量、频繁的 DOM 操作**,如果先在 JavaScript 中模拟进行,然后再通过计算比对,找到**真正需要更新的节点**,这样就有可能减少**不必要**的 DOM 操作,从而提升渲染性能。但并不是所有的 DOM 操作**都能通过虚拟 DOM 提升性能**,比如**单次删除某个节点**,直接操作 DOM肯定比虚拟 DOM 计算比对之后再删除要快。总体而言,** 虚拟 DOM 提升了 DOM 操作的性能下限,降低了DOM 操作的性能上限**。 所以会看到一些对渲染性能要求比较高的场景,比如**在线文档、表格编辑,还是会使用原生 DOM 操作**。 3. JavaScript 对象来表示 DOM 元素的方式,该对象仅包括常用的这些属性方法和节点关系,这样就可以大大降低对象内存、虚拟 DOM 差异对比的计算量等 >[danger] ##### vnode 1. **VNode是javascript对象**,VNode表示**Virtual DOM**(虚拟DOM)中的**虚拟节点** * js表现形式 ~~~ { sel: "div", data: {}, children: undefined, text: "Hello Virtual DOM", elm: undefined, key: undefined } ~~~ >[info] ## 总结 1. 要分清虚拟dom 和直接操作dom本质上的区别,在jq的时代需要开发既要**关心数据**,又要对对应的**dom进行操作**,例如新增数据的时候需要开发对所在**新增数据dom区域进行新增数据的dom创建并且插入**。那是否可以让jq也变成只关心数据dom直接改变? * 这里提出一个假设将对应的数据要渲染的**js模板**写好,当数据变化的时候整个该数据地方的dom全部重新渲染。这样我们就**可以不再关心模板只要关心数据**,但相对的问题我们需要将数据重新大批量重新加载这些中有些数据其实**没有变化应该不用刷新**,当然如果我们把这类情况考虑好自然就解决了这类问题 * 用js数据格式表现的数据结构成**为虚拟dom**,每次数据改变生成的新虚拟dom 结构和老的虚拟dom 结构进行比较哪里不一样重新渲染哪里(**这是理想状态下**)当然虚拟dom 开销也不能忽略 ~~~ let heroes = [ { name: "剑圣", age: 80, offsetTop: 0, elmHeight: 20 }, { name: "盲僧", age: 30, offsetTop: 0, elmHeight: 20 }, { name: "暗夜猎手", age: 50, offsetTop: 0, elmHeight: 20 }, { name: "寒冰射手", age: 20, offsetTop: 0, elmHeight: 20 }, { name: "赏金猎人", age: 40, offsetTop: 0, elmHeight: 20 } ]; function render() { let html = ""; heroes.forEach(hero => { html += ` <li class="hero" style="opacity: 0; transform: translateX(0px) translateY(${ hero.offsetTop }px)"> <div> <span class="left">姓名:${hero.name}</span> <span class="left l30">年龄:${hero.age}</span> <span class="right close">x</span> </div> </li> `; }); $(".content > ul").html(html); $(".content > ul > li").each((index, li) => { heroes[index].elmHeight = $(li).height(); }); heroes = heroes.reduce((arr, hero) => { let last = arr[arr.length - 1]; hero.offsetTop = last ? last.elmHeight + last.offsetTop + 10 : 10; return arr.concat(hero); }, []); ~~~ [jq案例链接](https://codesandbox.io/s/jq-demo-5i7qp?file=/src/index.js:25-1034) [虚拟dom链接](https://codesandbox.io/s/snabbdom-demo-forked-8lsvb?file=/src/index.js:769-944) >[danger] ##### 文章参考来源 [本段内容来自拉勾前端高手进阶](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=180#/detail/pc?id=3190) [参考内容来源](https://github.com/lagoufed/vuejs-enhancement)