白话虚拟dom

Posted nodejs全栈开发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了白话虚拟dom相关的知识,希望对你有一定的参考价值。

题图 By Clm From Bing


又到了发文章的时候了,今天和大家一起来讨论下虚拟dom,为什么要讨论这个玩意呢,因为现在最流行的两个前端框架都用到了虚拟dom。


首先咱们从起源说起,做web开发的时候,当数据发生变化的时候,我们要去更新dom,从而给前端用户呈现最新的数据,流程图如下:


白话虚拟dom

数据更改后直接操作dom,我们举例来看一下需求,比方说一个列表,列表下面有一个点击加载更多的按钮:


白话虚拟dom


点击加载更多,会发送一个ajax请求,请求更多的数据,然后将数据渲染到页面,我们一般会如何做呢,代码片段如下:


白话虚拟dom


我们看到,每次数据更新后,我们借助模板生成html片段,获取列表的新旧内容拼接,这里我们思考一下,就会发现已经存在的列表信息是不应该重新渲染的。


上面的案例先放一边,先了解一下什么是虚拟dom,简单来说就是一句话,用js对象来描述dom结构,示例如下:


有如下dom结构:


<ul id='list'> <li class='item'>item1</li> <li class='item'>item2</li></ul>


用js对象表示如下:


 {
tag: 'ul', attrs: { id:'list' }, children: [ { tag: 'li', attrs: { className: 'item' }, children: ['item1'] }, { tag: 'li', attrs: { className: 'item' }, children: ['item2'] } ] }


既然dom结构可以表示为js对象,那么根据js对象也可以生成dom结构。


再来看一张图,这张图我们将数据和ui生成一个js对象来表示dom结构也就是Virtual Dom,然后根据这个js对象(Virtual Dom),来生成真实的Dom;


白话虚拟dom


虚拟dom就是上面说的js对象,我们在数据和真实dom之间,加了一个中间层,类似夹心饼干。


这样做有什么好处呢?我们观察上面的流程,好像比直接操作dom还复杂,不要急,这里我们还有引入另外一个概念,diff算法,为什么要引入这个diff算法呢?


这是因为虚拟dom的渲染机制,那什么是虚拟dom的渲染机制呢?


当我们更改了数据后,并不是我们想的那样:立即成新的虚拟dom,然后根据新的虚拟dom渲染成真实Dom。而是数据更改后,在生成新的虚拟dom后,通过diff算法比较新旧虚拟dom,得出需要重新渲染的部分,然后最小化的更新真实dom。

流程如下图:


白话虚拟dom


然后,我们在看上面的点击加载更多案例,如果借助了虚拟dom,那么伪代码可坑就是这样操作了:



虚拟dom渲染流程用代码表示如下:


// 1. 构建虚拟DOMvar tree = el('div', {'id': 'container'}, [ el('h1', {style: 'color: blue'}, ['simple virtal dom']), el('p', ['Hello, virtual-dom']), el('ul', [el('li')])])
// 2. 通过虚拟DOM构建真正的DOMvar root = tree.render()document.body.appendChild(root)
// 3. 更改数据后生成新的虚拟DOMvar newTree = el('div', {'id': 'container'}, [ el('h1', {style: 'color: red'}, ['simple virtal dom']), el('p', ['Hello, virtual-dom']), el('ul', [el('li'), el('li')])])
// 4. 比较两棵虚拟DOM树的不同var patches = diff(tree, newTree)
// 5. 在真正的DOM元素上应用变更patch(root, patches)


说完虚拟dom,再次提到两个最火的前端框架,Vue和react,这两个框架都使用了虚拟dom,这两个框架所使用的虚拟dom有什么不同呢?


主要是更新虚拟dom的策略不同:


vue中将数据维护成了可观察的数据,数据的每一项都通过getter来收集依赖,然后将依赖转化成watcher保存在闭包中,数据修改后,触发数据的setter方法,然后调用所有的watcher修改旧的虚拟dom,从而生成新的虚拟dom,然后就是运用diff算法 ,得出新旧dom不同,根据不同更新真实dom。


而react的更新策略比较粗暴,可以用一个公式来表述,virtualDom = h(data),数据发生变化,通过调用setState生成新的虚拟dom,(而不是像vue一样为每个数据社会setter、getter、watcher),然后对比新旧dom,得出新旧dom不同,更新真实dom。但是,react给开发者暴露一个生命周期函数:shouldcomponentupdate,这个函数可以根据开发者的需求决定数据发生变化决定是否重新渲染。


react是 函数式组件思想,当你 setstate 就会遍历,diff 当前组件所有的子节点子组件, 这种方式开销是很大的,要合理使用shouldcomponentupdate。


vue为每个数据设置setter、getter、watcher,当数据足够多时,运行效率反倒不如react。


所以在选择框架时,要衡量一下,如果页面数据量大,变化多,选react,而如果项目中型,并且想快速开发,选vue。


以上便是笔者在当前阶段对虚拟dom的认识,其中不乏一些不足之处,如果你有什么建议或者想法欢迎留言。




欢迎关注、转发、点击好看。


https://www.jianshu.com/p/bef1c1ee5a0e

https://www.cnblogs.com/wubaiqing/p/6726429.html

https://blog.csdn.net/sinat_17775997/article/details/88287074

https://segmentfault.com/a/1190000015749345


以上是关于白话虚拟dom的主要内容,如果未能解决你的问题,请参考以下文章

关于React中的虚拟DOM与Diff算法

jquery 对象的 heightinnerHeightouterHeight 的区别以及DOM 元素的 clientHeightoffsetHeightscrollHeightoffset(代码片段

jQuery的DOM操作

50行代码实现虚拟DOM

JavaScript单行代码,也就是代码片段

白话系列之IOC,三个类实现简单的Ioc