首页 > 其他分享 >React循环DOM时为什么需要添加key

React循环DOM时为什么需要添加key

时间:2022-11-02 08:00:10浏览次数:85  
标签:DOM 元素 React mutation key 节点

一、React 渲染流程和更新流程

  • react渲染流程:jsx -> 虚拟dom -> 真实dom
  • react更新流程:props/state改变 -> render函数重新执行 -> 生成新的虚拟dom树 -> 新旧虚拟dom树进行diff -> 计算出差异进行更新 ->更新到真实的dom树

所以在每次更新的时候,React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI,如果一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法的复杂程度为 O(n3),其中 n 是树中元素的数量,如果在React中使用了该算法,那么展示1000个元素所需要执行的计算量将在十亿的量级范围,这个开销太过昂贵了,React的更新性能会变得非常低效;于是React对这个算法进行了优化,将其优化成了O(n),这也就是传说中的diff算法

二、diff 算法

diff 算法做了三处优化

  1. 同层节点之间相互比较,不会垮节点比较
  2. 不同类型的节点,产生不同的树结构
  3. 开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定

2-1 对比不同类型的元素

  • 当节点为不同的元素,React会拆卸原有的树,并且建立起新的树:当一个元素从<a>变成<img>,从<Article>变成<Comment>,或从<Button>变成<div>都会触发一个完整的重建流程

  • 当卸载一棵树时,对应的DOM节点也会被销毁,组件实例将执行 componentWillUnmount() 方法;

  • 当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中,组件实例将执行 componentWillMount()方法,紧接着 componentDidMount() 方法

  • 比如下面的代码更改:React 会销毁 Comment 组件并且重新装载一个新的组件,而不会对Counter进行复用;

<div>
    <Comment />
</div>

<span>
    <Comment />
</span>

2-2 对比同一类型的元素

  • 当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性

  • 比如下面的代码更改:通过比对这两个元素,React 知道只需要修改 DOM 元素上的 className 属性

<div className="before" title="stu" />
<div className="after" title="stu" />
  • 比如下面的代码更改:当更新 style 属性时,React 仅更新有所更变的属性。通过比对这两个元素,React 知道只需要修改 DOM 元素上的 color 样式,无需修改 fontWeight。
<div style={{color="red",fontWeight:"bold"}} />
<div style={{color="green",fontWeight:"bold"}} />
  • 如果是同类型的组件元素:组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps() 和 componentWillUpdate() 方法,下一步调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归;

2-3 对子节点递归

  • 在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个mutation(改变)。

  • 如果在最后插入一条数据的情况:前面两个比较是完全相同的,所以不会产生mutation,最后一个比较,产生一个mutation,将其插入到新的DOM树中即可,但是如果是在前面插入一条数据,React会对每一个子元素产生一个mutation,而不是保持 <li>星际穿越</li><li>盗梦空间</li>的不变;这种低效的比较方式会带来一定的性能问题,所以就得使用key来优化

后面插一条数据
<ul>
     <li>星际穿越</li>
     <li>盗梦空间</li>
 </ul>
 <ul>
     <li>星际穿越</li>
     <li>盗梦空间</li>
     <li>大话西游</li>
 </ul>

 前面插一条数据
 <ul>
     <li>星际穿越</li>
     <li>盗梦空间</li>
 </ul>
 <ul>
     <li>大话西游</li>
     <li>星际穿越</li>
     <li>盗梦空间</li>
 </ul>

参考:前端react面试题详细解答

三、key

要切记,在 diff 算法中,可以通过 key 来指定哪些节点在不同的渲染下保持稳定,并且要保证 key 是唯一的,不要使用随机数(随机数在下一次render时,会重新生成一个数字),也不能使用index,这都对性能是没有优化的

import React, { Component } from "react";

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      movies: ["星际穿越", "盗梦空间"],
    };
  }

  render() {
    return (
      <div>
        <h2>电影列表</h2>
        <ul>
          {this.state.movies.map((item, index) => {            return <li key={item}>{item}</li>;          })}        </ul>
        <button onClick={(e) => this.insertMovie()}>添加电影</button>
      </div>
    );
  }

  insertMovie() {
    this.setState({
      movies: ["大话西游", ...this.state.movies],
    });
  }
}

代码解析:在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个mutation。如果在movies后面添加数据,前面两个比较是完全相同的,所以不会产生mutation;最后一个比较,产生一个mutation,将其插入到新的DOM树中即可;
如果在movies前面添加数据,React会对每一个子元素产生一个mutation,而不是保持 <li>星际穿越</li><li>盗梦空间</li>的不变,这种低效的比较方式会带来一定的性能问题,
当子元素(这里的li)拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素:
在下面这种场景下,key为"星际穿越"和"盗梦空间"的元素仅仅进行位移,不需要进行任何的修改; 将key为"大话西游"的元素插入到最前面的位置即可;

标签:DOM,元素,React,mutation,key,节点
From: https://www.cnblogs.com/beifeng1996/p/16849780.html

相关文章

  • Javascript进阶笔记 - DOM模型与节点
    DOM模型与节点目录DOM模型与节点1.DOM模型2.节点2.1节点的常用方法1.DOM模型DOM(文档对象模型)就是把文档中的标签,属性,文本转换成对象来管理(类似于Java中的对象)do......
  • Javascript进阶笔记 - DOM的增删改查
    DOM的增删改查目录DOM的增删改查1.DOM查询1.1常规DOM查询方法1.2通过CSS选择器查找HTML元素1.3通过HTML对象选择器查找HTML对象2.DOM元素的增添3.DOM元素的修改......
  • Javascript进阶笔记 - DOM操作CSS样式
    DOM操作CSS样式目录DOM操作CSS样式1.操作样式2.获取当前样式3.其它样式相关属性1.操作样式可以通过JS修改元素的内联样式语法:元素.style.样式名=样式值注意:......
  • ansible推送ssh-key
    背景裸机安装完系统后使用手动方式发送ansible机器的ssh-key到其他主机总是不够方便想要找到一种更为简便的方式将key推送到其他主机方案:expect+shell/etc/ansible......
  • [ARC108E] Random IS
    仔细观察容易想到设\(f_{l,r,x,y}\)表示限制了区间\([l,r]\)种只能取\([x,y]\)中的数期望取多少个,看一下转移发下可能对最终答案有贡献的\([x,y]\)只可能是\([p_{......
  • DOM元素高度获取踩坑
    前情最近在开发一个需求,需要做一个滚动列表展示当前中奖用户,但是列表每一项高度又不是固定的,每次滚动前需要先获取当前要滚动的块是多高才知道要滚动多少。坑位在开发......
  • vue中key有什么作用(key的内部原理)
    虚拟DOM的key的作用:key是虚拟DOM对象的标识,当状态中的数据发生变化的时候,vue会根据新的数据生成新的虚拟DOM,随后vue进新虚拟DOM与旧虚拟DOM的差异比较(1)旧虚拟......
  • redis淘汰key的算法LRU与LFU的区别
    lru:leastrecentlyused,最近最少使用:淘汰很久没被访问的数据,以最近一次访问的时间做参考lfu:leastfrequentlyused,最不经常使用:淘汰最近一段时间被访问次数最少的数据......
  • key&key_len&ref&filtered(4)—mysql执行计划(五十)
    前面说了system是精确存储引擎和只存一条数据,const是主键和唯一索引才能达到的效率访问,ref是二级索引等值查询,或者联合索引全部等值,如果联合索引单个查询,则是index,ref_not是......
  • react项目因为代码复杂度问题无法打包
    项目中碰到个问题,后台返回数据为null,但是之前代码没有做null的判断,导致使用该数据里属性值时报错  很快,在代码中定位到报错字段,加上可选链操作符( ?.)时,代码编译运......