<div id="js_content">
<p style="text-align: left"><img src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-c56e90a6691bcf3be70411ff7a4d1499"></p>
<h2>一、什么是虚拟DOM</h2>
<p>虚拟 DOM (<code>Virtual DOM</code> )这个概念相信大家都不陌生,从 <code>React</code> 到 <code>Vue</code> ,虚拟 <code>DOM</code> 为这两个框架都带来了跨平台的能力(<code>React-Native</code> 和 <code>Weex</code>)</p>
<p>实际上它只是一层对真实<code>DOM</code>的抽象,以<code>JavaScript</code> 对象 (<code>VNode</code> 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上</p>
<p>在<code>Javascript</code>对象中,虚拟<code>DOM</code> 表现为一个 <code>Object</code>对象。并且最少包含标签名 (<code>tag</code>)、属性 (<code>attrs</code>) 和子元素对象 (<code>children</code>) 三个属性,不同框架对这三个属性的名命可能会有差别</p>
<p>创建虚拟<code>DOM</code>就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟<code>DOM</code>对象的节点与真实<code>DOM</code>的属性一一照应</p>
<p>在<code>vue</code>中同样使用到了虚拟<code>DOM</code>技术</p>
<p>定义真实<code>DOM</code></p>
<pre class="blockcode"><code class="language-go"><div id="app">
<p class="p">节点内容</p>
<h3>{<!-- -->{ foo }}</h3>
</div>
</code></pre>
<p>实例化<code>vue</code></p>
<pre class="blockcode"><code class="language-go">const app = new Vue({
el:"#app",
data:{
foo:"foo"
}
})
</code></pre>
<p>观察<code>render</code>的<code>render</code>,我们能得到虚拟<code>DOM</code></p>
<pre class="blockcode"><code class="language-go">(function anonymous() {
with(this){return _c('div',{attrs:{"id":"app"}},[_c('p',{staticClass:"p"},
[_v("节点内容")]),_v(" "),_c('h3',[_v(_s(foo))])])}})
</code></pre>
<p>通过<code>VNode</code>,<code>vue</code>可以对这颗抽象树进行创建节点,删除节点以及修改节点的操作, 经过<code>diff</code>算法得出一些需要修改的最小单位,再更新视图,减少了<code>dom</code>操作,提高了性能</p>
<h2>二、为什么需要虚拟DOM</h2>
<p><code>DOM</code>是很慢的,其元素非常庞大,页面的性能问题,大部分都是由<code>DOM</code>操作引起的</p>
<p>真实的<code>DOM</code>节点,哪怕一个最简单的<code>div</code>也包含着很多属性,可以打印出来直观感受一下:<img src="https://beijingoptbbs.oss-cn-beijing.aliyuncs.com/cs/5606289-11c92d61a74d2d5d4a295ba0b221951d"></p>
<p>由此可见,操作<code>DOM</code>的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验</p>
<p><strong>举个例子:</strong></p>
<p>你用传统的原生<code>api</code>或<code>jQuery</code>去操作<code>DOM</code>时,浏览器会从构建<code>DOM</code>树开始从头到尾执行一遍流程</p>
<p>当你在一次操作时,需要更新10个<code>DOM</code>节点,浏览器没这么智能,收到第一个更新<code>DOM</code>请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程</p>
<p>而通过<code>VNode</code>,同样更新10个<code>DOM</code>节点,虚拟<code>DOM</code>不会立即操作<code>DOM</code>,而是将这10次更新的<code>diff</code>内容保存到本地的一个<code>js</code>对象中,最终将这个<code>js</code>对象一次性<code>attach</code>到<code>DOM</code>树上,避免大量的无谓计算</p>
<blockquote>
<p>很多人认为虚拟 DOM 最大的优势是 diff 算法,减少 JavaScript 操作真实 DOM 的带来的性能消耗。虽然这一个虚拟 DOM 带来的一个优势,但并不是全部。虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种GUI</p>
</blockquote>
<h2>三、如何实现虚拟DOM</h2>
<p>首先可以看看<code>vue</code>中<code>VNode</code>的结构</p>
<p>源码位置:src/core/vdom/vnode.js</p>
<pre class="blockcode"><code class="language-go">export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
functionalContext: Component | void; // only for functional component root nodes
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: bool |
|