摘要:具体而言,就是每次数据发生变化,就重新执行一次整体渲染。而给出了解决方案,就是。由于只关注,通过阅读两个库的源码,对于的定位有了更深一步的理解。第二个而且,技术本身不是目的,能够更好地解决问题才是王道嘛。
前言
React 好像已经火了很久很久,以致于我们对于 Virtual DOM 这个词都已经很熟悉了,网上也有非常多的介绍 React、Virtual DOM 的文章。但是直到前不久我专门花时间去学习 Virtual DOM,才让我对 Virtual DOM 有了一定的理解,以致于要怀疑起很久之前看过的那些文章来。倒不是这些文章讲得不对,而是现在在我看来角度不太好,说得越多,越说不清。
让我能够有所开窍(自认为)的,是这篇文章:
Change And Its Detection In JavaScript Frameworks
Monday Mar 2, 2015 by Tero Parviainen
http://teropa.info/blog/2015/...
作者看问题的角度很棒,从数据变更与UI同步的角度来介绍各个典型框架,特别是对于 React 的 Virtual DOM,从这个角度理解起来更容易些。
感兴趣的同学,如果没有读过这篇文章,推荐去看一看,不感兴趣就算了。不过接下来我要讲的东西,部分整理自这篇文章,特别是从这篇文章中引用的图片,非常棒。当然还有我自己的一些思考,以及一些对于目前 Virtual DOM 实现的开源库的分析。
如果读了上面推荐的这篇文章,我倒是不介意你不再继续把本文读下去,因为有些东西你已经领会到了。当然,也不反对。
变化这件事谈论页面的变化之前,咱们先看下数据和页面(视觉层面的页面)的关系。数据是隐藏在页面底下,通过渲染展示给用户。同样的数据,按照不同的页面设计和实现,会以不同形式、样式的页面呈现出来。有时候在一个页面内的不同位置,也会有相同数据的不同表现。
Web 的早期,这些页面通常是静态的,页面内容不会变化。而如果数据发生了变化,通常需要重新请求页面,得到基于新的数据渲染出的新的页面。
至少,这个模式理解起来挺简单不是吗。
直到 Web 应用复杂起来,开发者们开始关注用户体验,开始将大量的处理向前端迁移,页面变得动态、灵活起来。一个显著的特征是,数据发生变化之后,不再需要刷新页面就能看到页面上的内容随之更新了。
前端需要做的事情变得多了起来,前端工程师们也就修炼了起来,各种前端技术也就出现了。
首先,聪明的工程师们发现既然是在前端渲染页面,如果只是部分数据发生了变化,就要把页面整体或一大块区域重新渲染就有点笨了。为什么不把事情做得更极致些,只更新变化的数据对应的页面的内容呢?
怎么做呢?操作 DOM 呗。DOM 就是浏览器提供给开发者用于操作页面的模型嘛,直接通过脚本来调用 DOM 的各种接口就 OK 了。而且我们还有了像 jQuery 这样的棒棒的工具,操作 DOM 变得 so easy。
然而,页面越来越复杂,聪明的工程师们发现数据变化之后,老是需要手动编码去操作对应的 DOM 节点执行更新,有点烦,不够懒啊。于是各种框架如雨后春笋般出现了,纷纷表示可以简化这个过程。
稍微早期的框架有这样的:
开发者借助框架,监听数据的变更,在数据变更后更新对应的 DOM 节点。虽然还是要写一些代码,但是写出来的代码好像很有条理的样子,至少更容易理解和维护了,也不错嘛。
更进一步,MVVM 框架出现了,以 AngularJS 为代表:
仍然是数据变化后更新对应 DOM 节点的方式,但是建立这种绑定关系的过程被框架所处理,开发者要写的代码变少了,而且代码更易读和维护了。
再然后呢,大家就在这个棒棒的模式上继续深耕,纷纷表示还可以在性能上做得更好,前端领域一片繁荣。
再后来 React 出现了,它不仅不是 MVVM 框架,甚至连 MV 框架都不是。这年头,不是个 MV 框架还好意思出门?可 React 还真的带来了新的思路!
什么思路呢?
就是回到过去,回到那个简单而美好的时候。具体而言,就是每次数据发生变化,就重新执行一次整体渲染。的确这样更简单,不用去琢磨到底是数据的哪一部分变化了,需要更新页面的哪一部分。但是坏处太明显,体验不好啊。而 React 给出了解决方案,就是 Virtual DOM。
Virtual DOM 概况来讲,就是在数据和真实 DOM 之间建立了一层缓冲。对于开发者而言,数据变化了就调用 React 的渲染方法,而 React 并不是直接得到新的 DOM 进行替换,而是先生成 Virtual DOM,与上一次渲染得到的 Virtual DOM 进行比对,在渲染得到的 Virtual DOM 上发现变化,然后将变化的地方更新到真实 DOM 上。
简单来说,React 在提供给开发者简单的开发模式的情况下,借助 Virtual DOM 实现了性能上的优化,以致于敢说自己“不慢”。
Virtual DOMReact 基于 Virtual DOM 的数据更新与UI同步机制:
初始渲染时,首先将数据渲染为 Virtual DOM,然后由 Virtual DOM 生成 DOM。
数据更新时,渲染得到新的 Virtual DOM,与上一次得到的 Virtual DOM 进行 diff,得到所有需要在 DOM 上进行的变更,然后在 patch 过程中应用到 DOM 上实现UI的同步更新。
Virtual DOM 作为数据结构,需要能准确地转换为真实 DOM,并且方便进行对比。除了 Virtual DOM 外,React 还实现了其他的特性,为了专注于 Virtual DOM,我另外找了两个比较 Virtual DOM 来学习:
virtual-dom
Snabbdom
这里也推荐给感兴趣且还没有读过两个库源码的同学。
由于只关注 Virtual DOM,通过阅读两个库的源码,对于 Virtual DOM 的定位有了更深一步的理解。
首先看数据结构。
Virtual DOM 数据结构
DOM 通常被视为一棵树,元素则是这棵树上的节点(node),而 Virtual DOM 的基础,就是 Virtual Node 了。
在 virtual-dom 中,给 Virtual Node 声明了对应的类 VirtualNode,基本是用于存储数据,包括:
tagName
properties
children
key
namespace
count
hasWidgets
hasThunks
hooks
descendantHooks
Snabbdom 的 Virtual Node 则是纯数据对象,通过 vnode 模块来创建,对象属性包括:
sel
data
children
text
elm
key
虽然有所差别,除去实现上的差别和库本身的额外特性,可以看到 Virtual Node 用于创建真实节点的数据包括:
元素类型
元素属性
元素的子节点
有了这些其实就可以创建对应的真实节点了。
创建 Virtual DOM
嵌套 Virtual Node 就可以得到一棵树了。virtual-dom 和 Snabbdom 都提供了函数调用的方式来创建 Virtual Tree,这个过程就是渲染了:
var vTree = h("div", [ h("span", "hello"), h("span", "world") ])
React 提供 JSX 这颗糖,使得我们可以用类似 HTML 的语法来编写,不过编译后实质还是通过函数调用来得到一棵嵌套的 Virtual Tree。而且这对于理解 Virtual DOM 机制来说不是特别重要,先不管这个。
使用 Virtual DOM
首先来看初始化,virtual-dom 提供了 createElement 函数:
var rootNode = createElement(tree) document.body.appendChild(rootNode)
根据 Virtual Node 创建真实 DOM 元素,然后再追加到页面上。
再来看更新。virtual-dom 有明确的两步操作,首先 diff,然后 patch:
var newTree = render(count) var patches = diff(tree, newTree) rootNode = patch(rootNode, patches)
而 Snabbdom 则简单些,只有一个 patch 函数,内部在进行比对的同时将更新应用到了真实 DOM 上,而且初始化也是用的 patch 函数:
var vnode = render(data) var container = document.getElementById("container") patch(container, vnode) // after data changed var newVnode = render(data) patch(vnode, newVnode)
性能优化
关于性能优化,除了 Virtual DOM 机制本身提供的特性以外,再就是不同的 Virtual DOM 库自身的优化方案了,这个可以看上面两个库的文档,不再赘述。
其实提到 Virtual DOM 的差异比对,有人会对其内部如何处理数组感兴趣。的确,如果数组元素的位置发生了改变,这个要识别起来是有点麻烦。为此,上面两个库和 React 其实都在 Virtual Node 上额外记录了一个属性“key”,就是用来辅助进行 Virtual Node 的比对的。
简单来说,如果两个 Virtual Node 的位置不同,但是 key 属性相同,那么会将这两个节点视为由相同数据渲染得到的,然后进一步进行差异分析。所以,并不是仅仅按照位置进行比对,具体的实现可以查看各个库的源码。
小结OK,以上就是我要讲的全部所有内容了。
相信很多同学之前对 Virtual DOM 已经很熟悉了,比我理解得更深入的同学相信也不会少。不过从“数据变化与UI同步更新”这个角度来理解 Virtual DOM,在我看来是比较好的,所以整理在这里了。
有个问题挺常见,AngularJS 和 React 哪个更好?
如果说各有千秋的话,估计大家就“呵呵”了。但是这两个框架/库从“数据变化与UI同步更新”的角度来看,的确都解决了问题,而且解决问题的方式大家都挺认可(至少在喜欢它们的同学眼里是这样的)。
而且,如果大家关注 Vue 的话,可以看到,这个 MVVM 框架已经发布了 2.0,其中就采用了 Virtual DOM 实现其UI同步更新!所以,这的确不矛盾啊。
第二个而且,技术本身不是目的,能够更好地解决问题才是王道嘛。
原文:一起理解 Virtual DOM
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/91233.html
摘要:借鉴,等的思想,引入状态管理在状态管理方面借鉴的思想,实现了单向数据流的管理。引入和算法总所周知,由于元素的庞大,以及操作容易引起页面重排的原因,直接操作性能是非常非常差的。然后通过算法对比新旧虚拟树,对真实进行最小单位的修改。 最近笔者打算自己造一个轮子,写一个mvvm的库,锻炼自己的能力,毕竟用库谁都可以,但是开发库的能力不是谁都有,不过笔者不是什么大神,所以更多的是希望在这个过程...
摘要:二原理每个都有两个,一个是新的,一个是原来的。三实现过程四算法的理解与实现本质上就是在和之间做了一个缓存。将差异的应用到真正的树上对真实上的树进行深度优先遍历,在所有的差异列表中找出当前遍历的节点差异,然后根据不同进行操作。 React Virtual DOM 一、概念 在react中,对于每个DOM对象都有一个相应的虚拟DOM对象,相当于DOM对象的轻量级副本 由于是Virtual...
摘要:关于前端框架大家都有了解,或多或少的使用过,比如,,等等。那么你是否也想自己手写一个的前端框架呢,我们从入手,手把手教你写基于的前端框架,在整个编写的过程中,希望大家学习更多,理解更多。本节我们以打包工具结合转换插件实现数据的抽象。 关于MVVM前端框架大家都有了解,或多或少的使用过,比如Angular,React,VUE等等。那么你是否也想自己手写一个MVVM的前端框架呢,我们从Vi...
摘要:既然看不懂,那就看看社区前辈们写的一些源码分析文章以及实现思路吧,又这么过了几天,总算是摸清点思路,于是在参考了前辈们的基础上,实现了一个简易版的。总结以上就是实现一个的总体思路,下节我们重点放在不同的上。 写在开头 工作中使用react也很长一段时间了,虽然对它的用法,原理有了一定的了解,但是总感觉停留在表面。本着知其然知其所以然的态度,我试着去看了react源码,几天下来,发现并不...
阅读 784·2023-04-26 00:30
阅读 2689·2021-11-23 09:51
阅读 1043·2021-11-02 14:38
阅读 2558·2021-09-07 10:23
阅读 2242·2021-08-21 14:09
阅读 1358·2019-08-30 10:57
阅读 1603·2019-08-29 11:20
阅读 1149·2019-08-26 13:53