首页IT科技虚拟资本与虚拟概论(如何理解虚拟DOM)

虚拟资本与虚拟概论(如何理解虚拟DOM)

时间2025-07-05 06:39:12分类IT科技浏览5177
导读:一、js 操作DOM 假如现在你需要写一个像下面一样的表格的应用程序,这个表格可以根据不同的字段进行升序或者降序的展示。...

一              、js 操作DOM

假如现在你需要写一个像下面一样的表格的应用程序               ,这个表格可以根据不同的字段进行升序或者降序的展示               。

这个应用程序看起来很简单                     ,你可以想出好几种不同的方式来写                     。最容易想到的可能是       ,在你的 JavaScript 代码里面存储这样的数据:

var sortKey = "new" // 排序的字段               ,新增(new)                      、取消(cancel)       、净关注(gain)       、累积(cumulate)人数 var sortType = 1 // 升序还是逆序 var data = [{...}, {...}, {..}, ..] // 表格数据

用三个字段分别存储当前排序的字段                      、排序方向              、还有表格数据;然后给表格头部加点击事件:当用户点击特定的字段的时候                     ,根据上面几个字段存储的内容来对内容进行排序       ,然后用 JS 或者 jQuery 操作 DOM        ,更新页面的排序状态(表头的那几个箭头表示当前排序状态                     ,也需要更新)和表格内容       。

这样做会导致的后果就是              ,随着应用程序越来越复杂        ,需要在JS里面维护的字段也越来越多                      ,需要监听事件和在事件回调用更新页面的DOM操作也越来越多              ,应用程序会变得非常难维护               。后来人们使用了 MVC       、MVP 的架构模式,希望能从代码组织方式来降低维护这种复杂应用程序的难度                     。但是 MVC 架构没办法减少你所维护的状态                      ,也没有降低状态更新你需要对页面的更新操作(前端来说就是DOM操作)                     ,你需要操作的DOM还是需要操作,只是换了个地方       。

既然状态改变了要操作相应的DOM元素               ,为什么不做一个东西可以让视图和状态进行绑定                     ,状态变更了视图自动变更       ,就不用手动更新页面了        。这就是后来人们想出了 MVVM 模式               ,只要在模版中声明视图组件是和什么状态进行绑定的                     ,双向绑定引擎就会在状态更新的时候自动更新视图                     。

MVVM 可以很好的降低我们维护状态 -> 视图的复杂程度(大大减少代码中的视图更新逻辑)              。但是这不是唯一的办法       ,还有一个非常直观的方法        ,可以大大降低视图更新的操作:一旦状态发生了变化                     ,就用模板引擎重新渲染整个视图              ,然后用新的视图更换掉旧的视图        。就像上面的表格        ,当用户点击的时候                      ,还是在JS里面更新状态              ,但是页面更新就不用手动操作 DOM 了,直接把整个表格用模版引擎重新渲染一遍                      ,然后设置一下innerHTML就完事了                      。

听到这样的做法                     ,经验丰富的你一定第一时间意识这样的做法会导致很多的问题              。最大的问题就是这样做会很慢,因为即使一个小小的状态变更都要重新构造整棵 DOM               ,性价比太低;而且这样做的话                     ,input和textarea的会失去原有的焦点。最后的结论会是:对于局部的小视图的更新       ,没有问题(Backbone就是这么干的);但是对于大型视图               ,如全局应用状态变更的时候                     ,需要更新页面较多局部视图的时候       ,这样的做法不可取                      。

但是这里要明白和记住这种做法        ,因为后面你会发现                     ,其实 Virtual DOM 就是这么做的              ,只是加了一些特别的步骤来避免了整棵 DOM 树变更                     。

另外一点需要注意的就是        ,上面提供的几种方法                      ,其实都在解决同一个问题:维护状态              ,更新视图。在一般的应用当中,如果能够很好方案来应对这个问题                      ,那么就几乎降低了大部分复杂性               。

DOM是很慢的                     。如果我们把一个简单的div元素的属性都打印出来                     ,你会看到:

 而这仅仅是第一层       。真正的 DOM 元素非常庞大,这是因为标准就是这么设计的               。而且操作它们的时候你要小心翼翼               ,轻微的触碰可能就会导致页面重排                     ,这可是杀死性能的罪魁祸首                     。

相对于 DOM 对象       ,原生的 JavaScript 对象处理起来更快               ,而且更简单       。DOM 树上的结构                      、属性信息我们都可以很容易地用 JavaScript 对象表示出来:

var element = { tagName: ul, // 节点标签名 props: { // DOM的属性                     ,用一个对象存储键值对 id: list }, children: [ // 该节点的子节点 {tagName: li, props: {class: item}, children: ["Item 1"]}, {tagName: li, props: {class: item}, children: ["Item 2"]}, {tagName: li, props: {class: item}, children: ["Item 3"]}, ] }

上面对应的HTML写法是:

<ul id=list> <li class=item>Item 1</li> <li class=item>Item 2</li> <li class=item>Item 3</li> </ul>

既然原来 DOM 树的信息都可以用 JavaScript 对象来表示       ,反过来        ,你就可以根据这个用 JavaScript 对象表示的树结构来构建一棵真正的DOM树        。

之前的章节所说的                     ,状态变更->重新渲染整个视图的方式可以稍微修改一下:用 JavaScript 对象表示 DOM 信息和结构              ,当状态变更的时候        ,重新渲染这个 JavaScript 的对象结构                     。当然这样做其实没什么卵用                      ,因为真正的页面其实没有改变              。

但是可以用新渲染的对象树去和旧的树进行对比              ,记录这两棵树差异        。记录下来的不同就是我们需要对页面真正的 DOM 操作,然后把它们应用在真正的 DOM 树上                      ,页面就变更了                      。这样就可以做到:视图的结构确实是整个全新渲染了                     ,但是最后操作DOM的时候确实只变更有不同的地方              。

这就是所谓的 Virtual DOM 算法。包括几个步骤:

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中 当状态变更的时候               ,重新构造一棵新的对象树                      。然后用新的树和旧的树进行比较                     ,记录两棵树差异 把2所记录的差异应用到步骤1所构建的真正的DOM树上       ,视图就更新了

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存                     。可以类比 CPU 和硬盘               ,既然硬盘这么慢                     ,我们就在它们之间加个缓存:既然 DOM 这么慢       ,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM)        ,最后的时候再把变更写入硬盘(DOM)               。

二              、虚拟DOM

虚拟 DOM (Virtual DOM )是由普通的JS对象来描述DOM对象                     ,因为不是真实的DOM对象              ,所以叫Virtual DOM                     。

实际上它只是一层对真实DOM的抽象        ,以JavaScript 对象 (VNode 节点) 作为基础的树                      ,用对象的属性来描述节点              ,最终可以通过一系列操作使这棵树映射到真实环境上       。

在Javascript对象中,虚拟DOM 表现为一个 Object对象               。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性                      ,不同框架对这三个属性的名命可能会有差别                     。

创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中                     ,所以虚拟DOM对象的节点与真实DOM的属性一一照应       。

在vue中同样使用到了虚拟DOM技术,

定义真实DOM

<div id="app"> <p class="p">节点内容</p> <h3>{{ foo }}</h3> </div>

实例化vue

const app = new Vue({ el:"#app", data:{ foo:"foo" } })

 观察render的render               ,我们能得到虚拟DOM

(function anonymous() { with(this){return _c(div,{attrs:{"id":"app"}},[_c(p,{staticClass:"p"}, [_v("节点内容")]),_v(" "),_c(h3,[_v(_s(foo))])])}})

通过VNode                     ,vue可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作       , 经过diff算法得出一些需要修改的最小单位,再更新视图               ,减少了dom操作                     ,提高了性能        。

官网: 渲染函数 & JSX — Vue.js (vuejs.org)

Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM                     。请仔细看这行代码:

return createElement(h1, this.blogTitle)

createElement 到底会返回什么呢?其实不是一个实际的 DOM 元素              。它更准确的名字可能是 createNodeDescription       ,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点        ,包括及其子节点的描述信息        。我们把这样的节点描述为“虚拟节点 (virtual node)               ”                     ,也常简写它为“VNode                     ”                      。“虚拟 DOM       ”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼              。

三                      、为什么需要虚拟DOM?

模板引擎没有解决跟踪状态变化的问题是因为              ,当数据发生变化后        ,无法获取上一次的状态                      ,只好把界面上的元素删除重新创建(可能造成闪烁              ,性能较低)。

具备跨平台的优势

由于虚拟DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力                      ,比如说浏览器平台                      、Weex、Node 等                     ,是实现ssr              、小程序等的基础                      。

提升渲染性能                     。

因为DOM是一个很大的对象,直接操作DOM               ,即便是一个空的 div 也要付出昂贵的代价                     ,执行速度远不如我们抽象出来的 Javascript 对象的速度快       ,因此               ,把大量的DOM操作搬运到 Javascript 中                     ,运用diff算法来计算出真正需要更新的节点       ,最大限度地减少DOM操作        ,从而显著提高性能。虚拟DOM的优势不在于单次的操作                     ,而是在大量                      、频繁的数据更新下              ,能够对视图进行合理       、高效的更新               。

四              、如何利用虚拟DOM来更新真实DOM?—— diff

diff(different)        ,顾名思义                      ,在构建DOM的过程中              ,会由diff过程就是比对计算DOM变动的地方,核心是由patch算法将变动映射到真实DOM上                      ,所以视图的创建更新流程就是下面这样:

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树                     ,插到文 档当中 当状态变更的时候,重新构造一棵新的对象树                     。然后用新的树和旧的树进行比较(diff过程)               ,记录两棵树差异 把2所记录的差异应用到步骤1所构建的真正的DOM树上(patch)                     ,视图就更新了

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
python numpy array拼接越长越慢(python中numpy.arange()函数的使用方法) 如何提高网站seo排名(提升网站SEO搜索排名的7种方法)