React 好像已经火了好久好久,以至于咱们对于 Virtual DOM 这个词都已经很熟悉了,网上也有很是多的介绍 React、Virtual DOM 的文章。可是直到前不久我专门花时间去学习 Virtual DOM,才让我对 Virtual DOM 有了必定的理解,以至于要怀疑起好久以前看过的那些文章来。倒不是这些文章讲得不对,而是如今在我看来角度不太好,说得越多,越说不清。javascript
让我可以有所开窍(自认为)的,是这篇文章:html
Change And Its Detection In JavaScript Frameworks
Monday Mar 2, 2015 by Tero Parviainen
http://teropa.info/blog/2015/...前端
做者看问题的角度很棒,从数据变动与UI同步的角度来介绍各个典型框架,特别是对于 React 的 Virtual DOM,从这个角度理解起来更容易些。java
感兴趣的同窗,若是没有读过这篇文章,推荐去看一看,不感兴趣就算了。不过接下来我要讲的东西,部分整理自这篇文章,特别是从这篇文章中引用的图片,很是棒。固然还有我本身的一些思考,以及一些对于目前 Virtual DOM 实现的开源库的分析。node
若是读了上面推荐的这篇文章,我却是不介意你再也不继续把本文读下去,由于有些东西你已经领会到了。固然,也不反对。git
谈论页面的变化以前,我们先看下数据和页面(视觉层面的页面)的关系。数据是隐藏在页面底下,经过渲染展现给用户。一样的数据,按照不一样的页面设计和实现,会以不一样形式、样式的页面呈现出来。有时候在一个页面内的不一样位置,也会有相同数据的不一样表现。github
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 实现了性能上的优化,以至于敢说本身“不慢”。
React 基于 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,经过阅读两个库的源码,对于 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同步更新!因此,这的确不矛盾啊。
第二个并且,技术自己不是目的,可以更好地解决问题才是王道嘛。