聊一聊React中虚拟DOM
<h3 id="1-什么是虚拟-dom">1. 什么是虚拟 DOM</h3><p>在 React 中实际上是 render 函数中return 的内容会生成 DOM,return 中的内容由两部分组成,一部分是 JSX ,另一部分就是 state 中的数据,所以简单来讲,在 React 中 JSX 结合 state 就生成了 DOM。</p>
<p>现在抛开虚拟 DOM 不谈,如果让我们去实现 React 中当数据发生变化时如何操作 DOM 来实现页面内容的变化,我们会怎样去实现?</p>
<p><strong>第一种方案:</strong></p>
<p>1)JSX + state 生成真实的 DOM,并显示在页面上</p>
<p>2)state 发生变化</p>
<p>3)此时 JSX + state 再次结合生成新的真实的 DOM</p>
<p>4)新的 DOM 直接替换掉原来的 DOM</p>
<p>这样页面会发生变化,但是生成真实的 DOM 和在页面上再重新加载新的 DOM 都比较耗性能。</p>
<p><strong>第二种方案:</strong></p>
<p>1)JSX + state 生成真实的 DOM,并显示在页面上</p>
<p>2)state 发生变化</p>
<p>3)此时 JSX + state 再次结合生成新的真实的 DOM</p>
<p>4)新的 DOM 和原始的 DOM 作对比,找出差异</p>
<p>5)利用找出的差异,替换掉页面上原始 DOM 的相应部分</p>
<p>此时页面也会发生变化,和方案一相比多了对比步骤但是只需要替换掉原始DOM的一部分即可,综合来说,方案二要优于方案一。</p>
<p><strong>第三种方案</strong></p>
<p>1)JSX + state 生成虚拟 DOM(虚拟 DOM 就是一个 JS 对象,用它来描述真实 DOM)</p>
<p>例如下面这段代码:</p>
<pre><code class="language-html"><div id='abc'>item</div>
</code></pre>
<p>注意上面的 <code>div</code>,<code>span</code> 标签时 JSX 语法,并不是真实的 DOM,这里是先生成虚拟 DOM ,然后再下一步的时候才用虚拟 DOM 生成真实的 DOM,由 JSX 到真实的 DOM 中间有一个虚拟 DOM。</p>
<pre><code class="language-js">JSX -> 虚拟DOM(JS对象) -> 真实DOM
</code></pre>
<p>也就是说,JSX 需要先转换为 JS 对象,然后再转换为真实的 DOM。</p>
<p>生成的虚拟 DOM 为</p>
<pre><code class="language-js">['div',{id: 'abc'}, 'item']
</code></pre>
<p>虚拟 DOM 的格式为</p>
<pre><code class="language-js">['标签名',标签属性对象,子标签]
</code></pre>
<p>那么 <code><div id='abc'>item</div></code> 是如何转化为 JS 对象的呢?</p>
<p>实际上在 React 中上面这样写就相当于下面这样写:</p>
<pre><code class="language-js">React.createElement('div', {id: 'abc'}, 'item');
</code></pre>
<p>那么实际上就算是没有 JSX 语法通过上面这样写也是可以的,但是会非常不方便。</p>
<p>2)用虚拟 DOM 的结构生成真实的 DOM 显示在页面上。</p>
<p>3)JSX + state 生成新的虚拟 DOM</p>
<p>4)两个虚拟 DOM 进行对比,找出差异</p>
<p>5)根据差异直接修改替换页面上的 DOM</p>
<p>虚拟 DOM 是一个 JS 对象,生成一个虚拟 DOM 比生成一个真实的 DOM 结构要容易省时地多,而且两个虚拟 DOM(JS 对象) 之间的对比也比较简单,所以方案三最佳。</p>
<p>React 中使用的也是第三种方案的思想。</p>
<h3 id="2-虚拟-dom-的优点">2. 虚拟 DOM 的优点</h3>
<p>那么虚拟DOM的优点到底有哪些呢?</p>
<p>1)性能提升</p>
<p>这一点通过上面的比较就可以看得出来</p>
<p>2)使得跨端应用得以实现,例如原生应用。</p>
<p>React Native 能够做原生应用虚拟 DOM 是很重要的一方面,原生应用中是没有 DOM 这个概念的,DOM 是浏览器中存在的,但是有了虚拟 DOM(JS 对象) 之后,在原生应用中就可以将虚拟 DOM(JS 对象) 转换为一些原生应用中能够支持的原生组件在原生应用中显示。</p>
<h3 id="3-虚拟-dom-的对比">3. 虚拟 DOM 的对比</h3>
<p>使用虚拟 DOM 时很重要的一个步骤就是两个虚拟 DOM 之间的比较,那么怎样去进行比较呢?</p>
<p>React 中采用 diff 算法,简单来说主要有以下三个方面:</p>
<p>1)当短时间内连续调用多次 setState 时,React 只会进行一次虚拟 DOM 的比对。</p>
<p>我们知道当 state 或者 props 发生变化时,页面会发生变化,实际上 props 的变化也是因为父组件 state 的变化,所以当页面发生变化时实际上是调用 setState 导致数据发生变化变化时。当短时间内连续调用多次 setState 时,如果每次都进行一次虚拟 DOM 的比对,那么性能会比较低,反之多次调用 setState 只进行一次虚拟 DOM 的比对会提升性能。这也是为什么 setState 要设置成异步的原因,因为如果同步的话当执行完一次 setState 时就会发生一次虚拟 DOM 的比对。(同步是顺序立即执行,异步是当所有的同步程序执行完后再执行)</p>
<p>2)在比较虚拟 DOM 时采用逐层同层比较,当上一层出现差异时,那么下面的各层就不需要再比较了,下面各层的 DOM 都将被新的 DOM 替换。</p>
<p>这样做看起来,复用性不是很好,因为下面各层有可能会有许多相同的 DOM。但是这样做会使得比较算法非常简单,比较的速度非常快。</p>
<p>3)设置 key 值</p>
<p>假设现在有一个数组 <code></code> 遍历每一项显示在页面上,现在数组发生变化将第一项 a 删掉,如果没有 key 值,数组 <code></code> 无法和原数组进行比对,例如 b 到底和原数组的哪一个进行比较呢?</p>
<p>但是现在假设有了 key 值,原数组中 a 的 key 值是 a,b 的 key 值是 b,c 的 key 值是 c。删除 a 之后,通过 key 值,b 的 key 值 b 在原数组中找到 b,说明 b 没有发生变化,c 同理也没有发生变化,但是原数组中的 a 在新数组中并没有找到,说明新数组中将 a 删掉了,所以在操作页面时将 a 删掉即可。</p>
<p>这里有一点需要注意的是,key 值一定要选不能变化的,利用数组的索引来做 key 值就不可取。还是以上面为例进行说明。原数组的 a 的 key 值是 0,b 的 key 值是 1,c 的 key 值是 2,删掉 a 后,新数组的 b 的 key 值是 0,c 的 key 值是 1,经过比对原数组的 a 和新数组的 b key 值相同,虚拟 DOM 会认为它们是相同的,没有差异,但是实际上它们是不同的。</p>
</div>
<div id="MySignature" role="contentinfo">
<div>
author:
zgc
</div>
<div>
origin:
http://www.cnblogs.com/zhangguicheng/
</div>
<p>-------------------------------------------</p>
<p>个性签名:梦想不只是梦与想</p>
<p>如果您觉得这篇文章哪个地方不恰当甚至有错误的话,麻烦告诉一下博主哦,感激不尽。</p>
<p>如果您觉得这篇文章对你有一点小小的帮助的话,希望能在右下角点个<span>“推荐”</span>哦。</p><br><br>
来源:https://www.cnblogs.com/zhangguicheng/p/12520986.html
頁:
[1]