React中key是什么,有什么用处?

当我们对子节点进行递归的时候,没有给li元素设置key,当渲染到页面的时候就会报警告。

<ul>
    {fruitsArr.map(item => {
        return <li>{item}</li>
    })}
</ul>

在这里插入图片描述
意思就是每一个在列表中的子孩子都必须有唯一的key。官网中写到“ key 不需要全局唯一,但在列表中需要保持唯一。”

当上面的代码中添加了key以后,页面就不会报警告了。

<ul>
    {fruitsArr.map(item => {
        return <li key={item}>{item}</li>
   	})}
</ul>

那么有些人,可能直接就用遍历时候的下标当做key值。官网中有一段话我们可以借鉴一下:
你也可以使用元素在数组中的下标作为key。这个策略在元素下不进行重新排序时比较合适,但一旦有顺序修改,diff就会变得慢。
当基于下标的组件进行重新排序时,组件state可能会遇到一些问题。由于组件实例是基于它们的key来决定是否更新以及复用,如果key是一个下标,那么修改顺序时会修改当前的key,导致非受控组件的state(比如输入框)可能相互篡改导致无法预期的变动。(官网有例子可以参考)

同时我们去看react的源码,packages > react-reconciler > src > ReactChildFiber.js > reconcileSingleElement以及deleteRemainingChildren的方法,就可以看出key作用

function reconcileSingleElement(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    element: ReactElement,
    lanes: Lanes,
  ): Fiber {
  // 获取当前元素key值
    const key = element.key;
    let child = currentFirstChild;
    while (child !== null) {
      // TODO: If key === null and child.key === null, then this only applies to
      // the first item in the list.
      // currentFirstChild的key与当前元素key值进行比较,也就是说,先对key值做第一层比较,然后做下面的操作。
      if (child.key === key) {
        switch (child.tag) {
          case Fragment: {
            if (element.type === REACT_FRAGMENT_TYPE) {
              deleteRemainingChildren(returnFiber, child.sibling);
              const existing = useFiber(child, element.props.children);
              existing.return = returnFiber;
              if (__DEV__) {
                existing._debugSource = element._source;
                existing._debugOwner = element._owner;
              }
              return existing;
            }
            break;
          }
          case Block:
            if (enableBlocksAPI) {
              let type = element.type;
              if (type.$$typeof === REACT_LAZY_TYPE) {
                type = resolveLazyType(type);
              }
              if (type.$$typeof === REACT_BLOCK_TYPE) {
                // The new Block might not be initialized yet. We need to initialize
                // it in case initializing it turns out it would match.
                if (
                  ((type: any): BlockComponent<any, any>)._render ===
                  (child.type: BlockComponent<any, any>)._render
                ) {
                  deleteRemainingChildren(returnFiber, child.sibling);
                  const existing = useFiber(child, element.props);
                  existing.type = type;
                  existing.return = returnFiber;
                  if (__DEV__) {
                    existing._debugSource = element._source;
                    existing._debugOwner = element._owner;
                  }
                  return existing;
                }
              }
            }
          // We intentionally fallthrough here if enableBlocksAPI is not on.
          // eslint-disable-next-lined no-fallthrough
          default: {
            if (
              child.elementType === element.type ||
              // Keep this check inline so it only runs on the false path:
              (__DEV__
                ? isCompatibleFamilyForHotReloading(child, element)
                : false)
            ) {
              deleteRemainingChildren(returnFiber, child.sibling);
              const existing = useFiber(child, element.props);
              existing.ref = coerceRef(returnFiber, child, element);
              existing.return = returnFiber;
              if (__DEV__) {
                existing._debugSource = element._source;
                existing._debugOwner = element._owner;
              }
              return existing;
            }
            break;
          }
        }
        // Didn't match.
        deleteRemainingChildren(returnFiber, child);
        break;
      } else {
        deleteChild(returnFiber, child);
      }
      child = child.sibling;
    }

    if (element.type === REACT_FRAGMENT_TYPE) {
      const created = createFiberFromFragment(
        element.props.children,
        returnFiber.mode,
        lanes,
        element.key,
      );
      created.return = returnFiber;
      return created;
    } else {
      const created = createFiberFromElement(element, returnFiber.mode, lanes);
      created.ref = coerceRef(returnFiber, currentFirstChild, element);
      created.return = returnFiber;
      return created;
    }
  }

所以key的主要作用来比较标记同级节点上当前元素的唯一性。然后对他进行相应的增删改查,加快渲染速度。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值