React——虚拟DOM与Diff算法(不用index为key赋值的原因)

React——虚拟DOM与Diff算法(不用index为key赋值的原因)

篇幅较长,但看完定会收获满满~

React的高性能很多来自于它的虚拟DOM,在项目初始化渲染,或处罚setState时,会触发render()函数来进行页面渲染,React并不会一个组件一个组件的向页面中插入DOM元素,而是在内存中创建一个DOM tree,等到所有新节点都放到了内存中的DOM tree中后,React会一次性的将其渲染到页面中,这样就只触发了一次DOM操作;

由于浏览器中DOM操作非常消耗性能,所以减少DOM操作自然也就成了优化性能更好的选择。

而就在rendr函数执行时,React并不会一次将虚拟DOm全部会知道页面中,而是通过比较,看哪些元素变化了,需要绘制,而没有变化的DOM元素就不绘制了,二者比较所使用的算法,就是鼎鼎有名的Diff算法。

Diff算法:
对比两棵DOM树时,首选判断两棵树的根节点,不同类型的根节点元素会有不同的形态;
如果根节点类型(div,table,…)不同,则属性与子元素等其他的就无需比较,直接全部替换就是最优解;
如果根节点类型(div,table,…)相同,则比较两者是否有不同的属性和属性值,只需改变不同的属性值,添加新的属性+属性值,删除减少的属性+属性值,无需更换节点。
在处理完当前节点时,React会继续用同样的方法递归器子节点。

优化Diff算法使用的小技巧:
在子元素末尾列表新增元素 开销较小

举例:

真实DOM:
<ul>
	<li class="li1">li>
	<li class="li2">li>
ul>
虚拟DOM:
<ul>
	<li class="li1">li>
	<li class="li2">li>
	<li class="li3">li>
ul>

Diff算法计算过程:
真 ----- 虚
ul === ul
li1 === li1
li2 === li2
’ ’ ! == li3

计算过后程序会知道,只需在真实DOM中的

  • 后面添加一个新元素
  • 即可。

    但如果将

  • 添加到
  • 前面呢?

    真实DOM:
    <ul>
    	<li class="li1">li>
    	<li class="li2">li>
    ul>
    虚拟DOM:
    <ul>
    	<li class="li3">li>
    	<li class="li1">li>
    	<li class="li2">li>
    ul>
    

    Diff算法计算过程:
    真 ----- 虚
    ul === ul
    li1 !== li3
    li2 !== li1
    ’ ’ ! == li2

    计算过后程序会知道,ul下面的所有li均不匹配,所以需要全部替换。

    这样就会浪费性能。

    但很多时候这种类似的情况我们没办法精确的优化Diff算法。

    但这也是可以解决的,就是为子元素添加key属性为其标记唯一性。

    比如上面的例子:

    真实DOM:
    <ul>
    	<li class="li1" key="key1">li>
    	<li class="li2" key="key1">li>
    ul>
    虚拟DOM:
    <ul>
    	<li class="li3" key="key3">li>
    	<li class="li1" key="key1">li>
    	<li class="li2" key="key2">li>
    ul>
    

    Diff算法认为key值相等的就是相同的元素,所以Diff算法计算过后,React知道只有key="key3"的元素是新元素,key="key1"与key="key2"的两个元素仅仅移动了位置,所以在对应位置插入key="key3"的元素就可以了。

    使用key属性的注意事项:

    key在当前列表中必须唯一,尽量不要用index与Math.random()为key赋值。个人习惯为每个需要遍历的数据都加上唯一的id值,遍历时用id为key赋值就好了。

    如果用index为其赋值,那么虚拟DOM与真实DOM遍历时会分别进行遍历,就会出现两次遍历导致相同元素会有不同key值的情况,此时Diff算法就会由于二者key值不同而认为它们是不同的:

    真实DOM:
    <ul>
    	<li class="li1" key="0">li>
    	<li class="li2" key="1">li>
    	<li class="li3" key="2">li>
    	<li class="li4" key="3">li>
    ul>
    虚拟DOM(删除了<li class="li2" key="2">li>):
    <ul>
    	<li class="li1" key="0">li>
    	<li class="li3" key="1">li>
    	<li class="li4" key="2">li>
    ul>
    

    Diff算法会由于key值的缘故认为




  • 是不同的,要全部替换,但实际上其实相同的。

    而Math.random()会为真实DOM与虚拟DOM的所有元素均添加不同的key,这意义并不大,或者说,只是让react不报错了而已;
    而且虽然Math.random()生成的数字范围极大,但也有很小的概率生成相同的数字,这就很有可能使Diff算法误认为两个不同的元素是相同的,一个严谨的项目不可以出现这种可以避免的漏洞!

    你可能感兴趣的:(React,reactjs,javascript)