[TOC] >[success] # 了解虚拟dom ~~~ 1.vue 无论是模板通过AST 转换成render形式也好,或者直接使用render的形式也罢,其实都是为 接下来转换'虚拟DOM' 做准备,在这之前需要了解什么是虚拟dom ~~~ >[info] ## 虚拟dom 是什么 ~~~ 1.是由普通的 JS 对象来描述 DOM 对象,因为不是真实的 DOM 对象,所以叫 Virtual DOM ~~~ >[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可以 让开发阶段更关心数据 ~~~ >[danger] ##### 为什么使用虚拟dom ~~~ 1.Vue是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM, 而操作真实DOM又是非常耗费性能的 2.手动操作DOM时代 ,即使有了jq帮助我们对dom做了简化的操作,但随着项目的复杂DOM操作也变得 复杂起来 3.后来也出现了一种产物模板方式进行dom操作,就例如'art-template',但依旧不能解决跟踪状态变化 4.开始思考,想跟踪变化,就需要进行比较,比较后知道哪里改变了,去改变对应位置思路就诞生了,但是 如果说维护两个dom,每次两个dom进行比较,这原本一个dom就相对比较浪费,现在又用两个,这个肯定 不行,如果我们用js形式记录两个数组,每次比较两个数组那个位置变化了,再去操作对应变化的局部的dom 是不是会更好(两个dom 比较不好的主要原因通过打印dom 属性看到的数量上,这么多属性每次都要最对比是个开销,并且并不是每个属性都用了) 5.Virtual DOM 的好处是当状态改变时不需要立即更新 DOM,只需要创建一个虚拟树来描述 DOM, Virtual DOM 内部将弄清楚如何有效(diff)的更新 DOM ~~~ * 虚拟dom 用js表现形式 ~~~ { sel: "div", data: {}, children: undefined, text: "Hello Virtual DOM", elm: undefined, key: undefined } ~~~ >[danger] ##### 虚拟dom 作用 ~~~ 1.维护视图和状态关系 2.复杂视图情况下提高性能,如果说页面只是一个很简单的视图状态,额外去多维护一个虚拟dom也就是 js的话其实很不值当 3.还可以实现 SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app) ~~~ >[danger] ##### 常见的操作虚拟dom 的库 ~~~ 1.'Snabbdom' Vue 2.x 内部使用的 Virtual DOM 就是改造的 Snabbdom 2.'virtual-dom',其实进入'virtual-dom'github在项目介绍中也有一段对虚拟dom的产生的动机描述 ~~~ * 谷歌翻译动机描述 ~~~ 手动DOM操作比较麻烦,并且很难跟踪以前的DOM状态。解决此问题的方法是编写代码, 就像在状态更改时重新创建整个DOM一样。当然,如果您每次更改应用程序状态时实际上都重新创建 了整个DOM,则应用程序将非常缓慢,并且输入字段将失去焦点。 virtual-dom是模块的集合,旨在提供声明性的方式来表示应用程序的DOM。因此,您不必在应用程序 状态更改时更新DOM,而只需创建一个虚拟树或VTree,它看起来像您想要的DOM状态。virtual-dom 然后,您将了解如何有效地使DOM看起来像这样,而无需重新创建所有DOM节点。 virtual-dom通过创建完整VTree的视图,然后有效地修补DOM以使其完全符合您的描述,您可以在状态 变化时更新视图。这样可以避免在应用程序代码中进行手动DOM操作和先前状态跟踪,从而为Web应用 程序提供了清晰且可维护的呈现逻辑。 ~~~ >[danger] ##### 虚拟dom 真的能提高性能么? [本段内容来自拉勾前端高手进阶](https://kaiwu.lagou.com/course/courseInfo.htm?courseId=180#/detail/pc?id=3190) ~~~ 1.优化性能 。DOM 操作是比较耗时的,对于大量、频繁的 DOM 操作,如果先在 JavaScript 中模拟进行, 然后再通过计算比对,找到真正需要更新的节点,这样就有可能减少不必要的 DOM 操作,从而提升渲染 性能。但并不是所有的 DOM 操作都能通过虚拟 DOM 提升性能,比如单次删除某个节点,直接操作 DOM 肯定比虚拟 DOM 计算比对之后再删除要快。总体而言, 虚拟 DOM 提升了 DOM 操作的性能下限,降低了 DOM 操作的性能上限。 所以会看到一些对渲染性能要求比较高的场景,比如在线文档、表格编辑,还是会 使用原生 DOM 操作。 2.跨平台 。由于虚拟 DOM 以 JavaScript 对象为基础,所以可根据不同的运行环境进行代码转换 (比如浏览器、服务端、原生应用等),这使得它具有了跨平台的能力。 ~~~ >[danger] ##### 总结 ~~~ 1.要分清虚拟dom 和直接操作dom本质上的区别,在'jq'的时代需要开发既要关心数据,又要对对应的'dom'进行 操作,例如新增数据的时候需要开发对所在新增数据'dom'区域进行新增数据的'dom'创建并且插入。那是否可以 让'jq' 也变成只关心数据'dom'直接改变?这里提出一个假设将对应的数据要渲染的'js模板'写好,当数据变化的时候 整个该数据地方的dom全部重新渲染。和之前相比原本100条数据新增一条我们只需要创建一条新增数据的dom 插入这100条即可,现在为了减少dom操作需要重新渲染101条数据'dom'并且重新插入,如果现在用虚拟dom来解决 这个问题我们每次会存储新老的dom结构,注意这里存储的不是webapi 提供的dom对象结构,而是将其抽象化 用js数据格式表现的数据结构成为虚拟dom,每次数据改变生成的新虚拟dom 结构和老的虚拟dom 结构进行比较 哪里不一样重新渲染哪里(这是理想状态下) 2.通过用 JavaScript 对象来表示 DOM 元素的方式,该对象仅包括常用的这些属性方法和节点关系,这样就可以大大降低对象内存、虚拟 DOM 差异对比的计算量等 ~~~ * jq直接操作数据 ~~~ 1.正常的jq代码以我的理解我是不会去像下面那么写,更多的是新增创建一个对应的新增dom,插入到他的父节点 ,下面案例站在的角度是vue这类不操组dom 直接数据变化影响视图用jq强行实现,就出现了已经被渲染的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://github.com/lagoufed/vuejs-enhancement)