React16源码: React中的reconcileChildIterator和reconcileChildrenArray的源码实现

【React源码 - Diff算法】 React源码之Diff算法详解 阅读详情

reconcileChildIterator 和 reconcileChildrenArray


1 )概述

  • 在react更新某一个节点的时候,要根据这个节点,它的类型去获取它的children
  • 比如说如果是 Function Component,它要调用这个 component 计算出它的return的属性
  • return的属性可能是一个数组,可能是单个的 ReactElement,可能是 number, string 这些类型
  • 要根据这些不同的类型去进行一些特殊的处理,然后得到它的children,和对应的 fiber 对象
  • 这样的话,又可以继续往下去迭代,迭代到最终的一个节点
  • 在经过这个过程,就可以把一个 fiber 树的一侧的子树进行完整的遍历和构建的过程
  • 在这个过程当中,有一个 children 的类型比较特殊,那就是数组
  • 在我们使用数组作为children返回的时候,都会看到这样的一个提醒
  • 那就是我们每一个节点上面必须要有一个key属性
  • 如果这个key属性没有,那 react 就会给我们一个提醒
  • 我们必须要对数组中的每一个节点使用一个唯一的key来标识它
  • 这个key和数组调和有特定的关系,主要关注:
    • A. key的作用
    • B. 对比数组 children 是否可复用的一个过程
    • C. generator 和 array遍历的区别

2 )源码

在 reconcileChildFibers 函数中调用的 reconcileChildrenArray

定位到 packages/react-reconciler/src/ReactChildFiber.js#L732

先看下 reconcileChildrenArray 这个API

新老children的对比过程,以及判断节点是否可复用的过程,这个过程会涉及到react的一个算法
尽量减少 数组的遍历次数,达到复用节点的过程

// 对于 newChildren 的遍历本质上是 O(n)
// 第一次遍历通过 newIdx++ 的方式,后面 特殊情况的判断来加速性能提升
// 只有在最后一个 for 循环中进行完整遍历,完成后创建节点
// 这里有很多判断一个节点是否可复用,都是通过 key 是否存在来判断的
// 如果不服用并且创建新的 fiber 节点,删除老的 fiber 节点 会导致内存申请和内存回收频繁
// 整体对性能存在影响,因为过于频繁的内存回收会导致内存抖动的问题
function reconcileChildrenArray(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  newChildren: Array<*>,
  expirationTime: ExpirationTime,
): Fiber | null {
  // This algorithm can't optimize by searching from boths ends since we
  // don't have backpointers on fibers. I'm trying to see how far we can get
  // with that model. If it ends up not being worth the tradeoffs, we can
  // add it later.

  // Even with a two ended optimization, we'd want to optimize for the case
  // where there are few changes and brute force the comparison instead of
  // going for the Map. It'd like to explore hitting that path first in
  // forward-only mode and only go for the Map once we notice that we need
  // lots of look ahead. This doesn't handle reversal as well as two ended
  // search but that's unusual. Besides, for the two ended optimization to
  // work on Iterables, we'd need to copy the whole set.

  // In this first iteration, we'll just live with hitting the bad case
  // (adding everything to a Map) in for every insert/move.

  // If you change this code, also update reconcileChildrenIterator() which
  // uses the same algorithm.

  if (__DEV__) {
    // First, validate keys.
    let knownKeys = null;
    for (let i = 0; i < newChildren.length; i++) {
      const child = newChildren[i];
      knownKeys = warnOnInvalidKey(child, knownKeys);
    }
  }

  // 声明一堆的变量
  let resultingFirstChild: Fiber | null = null;
  let previousNewFiber: Fiber | null = null;

  let oldFiber = currentFirstChild; // 上一次渲染的过程中,渲染完成后,当前节点的第一个child节点
  let lastPlacedIndex = 0;
  let newIdx = 0;
  let nextOldFiber = null;
  // 这个for循环以相同的顺序,分别遍历新老的children对应的节点,判断它的key是否相同
  // 如果不相同,则跳出循环,到这个节点为止
  // 在遍历新老数组的时候,找到第一个不能复用的节点,这时候就会跳出循环
  for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
    // 对于react渲染整个数组的过程中,会在每个 Fiber 节点上面 设置一个index属性 就是 这个节点在children里的位置
    // 老的children的Fiber的index > newIdx 说明它们的位置不匹配,则直接赋值给 nextOldFiber
    if (oldFiber.index > newIdx) {
      nextOldFiber = oldFiber;
      oldFiber = null;
    } else {
      // 正常情况下,取当前节点的下一个节点
      nextOldFiber = oldFiber.sibling;
    }
    const newFiber = updateSlot(
      returnFiber,
      oldFiber,
      newChildren[newIdx],
      expirationTime,
    );
    // 为 null 代表 这个节点不能复用 跳出 for循环
    if (newFiber === null) {
      // TODO: This breaks on empty slots like null children. That's
      // unfortunate because it triggers the slow path all the time. We need
      // a better way to communicate whether this was a miss or null,
      // boolean, undefined, etc.
      // 如果oldFiber 不存在,则处理成下一个节点
      if (oldFiber === null) {
        oldFiber = nextOldFiber;
      }
      break;
    }
    // 存在 newFiber 并且 shouldTrackSideEffects
    if (shouldTrackSideEffects) {
      // newFiber.alternate 不存在,说明它没有复用 oldFiber 来产生一个节点,而是直接 return 了一个新的Fiber
      // 如果重新复用的 oldFiber, 那 newFiber.alternate 应该存在的,这个节点至少经过一次渲染,是有 current 和 workInProgress 的存在的
      // 没有复用之前的节点,则说明 老的节点 失效的状况,则删除之
      if (oldFiber && newFiber.alternate === null) {
        // We matched the slot, but we didn't reuse the existing fiber, so we
        // need to delete the existing child.
        deleteChild(returnFiber, oldFiber);
      }
    }
    lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    // 每次循环都会给 previousNewFiber 赋值
    // 如果没有被赋值,代表是新节点
    if (previousNewFiber === null) {
      // TODO: Move out of the loop. This only happens for the first run.
      resultingFirstChild = newFiber; // 这里
    } else {
      // TODO: Defer siblings if we're not at the right index for this slot.
      // I.e. if we had null values before, then we want to defer this
      // for each null value. However, we also don't want to call updateSlot
      // with the previous one.
      previousNewFiber.sibling = newFiber; // 如果之前节点已经存在, newFiber 是之前节点previousNewFiber的兄弟节点
    }
    previousNewFiber = newFiber; // 这里进行赋值
    oldFiber = nextOldFiber; // 接着下一轮
  }

  // 跳出循环后,当两者相等,新数组的children 全部创建fiber对象了, 新数组已经操作完成了
  if (newIdx === newChildren.length) {
    // We've reached the end of the new children. We can delete the rest.
    deleteRemainingChildren(returnFiber, oldFiber); // 对于老数组情况,存在 oldFiber,删除剩下的节点
    return resultingFirstChild; // 返回第一个节点,第一个节点才是 return fiber 的 child 属性所指向的节点,剩下的后续节点都是通过 .sibling 来指向下去的
  }
  // oldFiber 为 null 时,说明老节点已经被遍历完了
  if (oldFiber === null) {
    // If we don't have any more existing children we can choose a fast path
    // since the rest will all be insertions.
    // 老的节点已经被复用完了,新节点还剩下一部分没有创建,对剩下的节点都进行创建
    // 就不需要关心它是否有复用的节点了
    for (; newIdx < newChildren.length; newIdx++) {
      const newFiber = createChild(
        returnFiber,
        newChildren[newIdx],
        expirationTime,
      );
      if (!newFiber) {
        continue;
      }
      // 同样对这些节点进行 placeChild 操作
      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
      if (previousNewFiber === null) {
        // TODO: Move out of the loop. This only happens for the first run.
        resultingFirstChild = newFiber;
      } else {
        previousNewFiber.sibling = newFiber; // 存在,则处理 sibling 指向
      }
      previousNewFiber = newFiber;
    }
    return resultingFirstChild;
  }

  // Add all children to a key map for quick lookups.
  // 剩下的情况是数组可能存在顺序的变化,oldFiber 可能还有一些兄弟节点,newChildren 还有几个没有被创建
  // 从 oldFiber 剩下的节点中找到 newChildren 可以复用的 Fiber 节点
  // 通过 mapRemainingChildren 来创建一个map
  const existingChildren = mapRemainingChildren(returnFiber, oldFiber);

  // Keep scanning and use the map to restore deleted items as moves.
  // 最后的遍历把所有节点创建一遍
  for (; newIdx < newChildren.length; newIdx++) {
    // 调用一个 
    const newFiber = updateFromMap(
      existingChildren,
      returnFiber,
      newIdx,
      newChildren[newIdx],
      expirationTime,
    );
    // 存在 newFiber 
    if (newFiber) {
      if (shouldTrackSideEffects) {
        // 存在 current,说明已经被复用了
        if (newFiber.alternate !== null) {
          // The new fiber is a work in progress, but if there exists a
          // current, that means that we reused the fiber. We need to delete
          // it from the child list so that we don't add it to the deletion
          // list.
          // 删除 map 里面的删除该匹配的节点
          existingChildren.delete(
            newFiber.key === null ? newIdx : newFiber.key,
          );
        }
      }
      // 没有被复用, 执行 placeChild
      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
      if (previousNewFiber === null) {
        resultingFirstChild = newFiber;
      } else {
        previousNewFiber.sibling = newFiber;
      }
      previousNewFiber = newFiber;
    }
  }

  if (shouldTrackSideEffects) {
    // Any existing children that weren't consumed above were deleted. We need
    // to add them to the deletion list.
    // 在 existingChildren 中遗留的 fiber 对象执行 删除,因为这些fiber对象没有被复用
    existingChildren.forEach(child => deleteChild(returnFiber, child));
  }

  return resultingFirstChild;
}
  • 下面是上面用到的几个API
    • 进入 updateSlot
      // 这个方法对比,新老的key是否相同,来查看它是否可以复用 老的fiber节点
      function updateSlot(
        returnFiber: Fiber,
        oldFiber: Fiber | null,
        newChild: any,
        expirationTime: ExpirationTime,
      ): Fiber | null {
        // Update the fiber if the keys match, otherwise return null.
        // oldFiber 存在的情况下,获取它的key
        const key = oldFiber !== null ? oldFiber.key : null;
        // 文本节点,没有key
        if (typeof newChild === 'string' || typeof newChild === 'number') {
          // Text nodes don't have keys. If the previous node is implicitly keyed
          // we can continue to replace it without aborting even if it is not a text
          // node.
          if (key !== null) {
            return null;
          }
          // 老的节点存在 key
          return updateTextNode(
            returnFiber,
            oldFiber,
            '' + newChild,
            expirationTime,
          );
        }
        // 对象类型,基于 $$typeof 来判断
        if (typeof newChild === 'object' && newChild !== null) {
          switch (newChild.$$typeof) {
            case REACT_ELEMENT_TYPE: {
              // 只有在前后的key相同的情况下,才会复用节点
              if (newChild.key === key) {
                if (newChild.type === REACT_FRAGMENT_TYPE) {
                  return updateFragment(
                    returnFiber,
                    oldFiber,
                    newChild.props.children,
                    expirationTime,
                    key,
                  );
                }
                return updateElement(
                  returnFiber,
                  oldFiber,
                  newChild,
                  expirationTime,
                );
              } else {
                // 不同,则停止,不能复用
                return null;
              }
            }
            // 下面也类似
            case REACT_PORTAL_TYPE: {
              if (newChild.key === key) {
                return updatePortal(
                  returnFiber,
                  oldFiber,
                  newChild,
                  expirationTime,
                );
              } else {
                return null;
              }
            }
          }
          // 继续判断是数组还是可迭代
          if (isArray(newChild) || getIteratorFn(newChild)) {
            if (key !== null) {
              return null;
            }
      
            return updateFragment(
              returnFiber,
              oldFiber,
              newChild,
              expirationTime,
              null,
            );
          }
      
          throwOnInvalidObjectType(returnFiber, newChild);
        }
      
        if (__DEV__) {
          if (typeof newChild === 'function') {
            warnOnFunctionType();
          }
        }
      
        return null;
      }
      
    • 进入 placeChild
      function placeChild(
        newFiber: Fiber,
        lastPlacedIndex: number,
        newIndex: number,
      ): number {
        // 同步 index
        newFiber.index = newIndex;
        if (!shouldTrackSideEffects) {
          // Noop.
          return lastPlacedIndex;
        }
        // 获取 current
        const current = newFiber.alternate;
        // 存在 current
        if (current !== null) {
          const oldIndex = current.index;
          if (oldIndex < lastPlacedIndex) {
            // This is a move.
            newFiber.effectTag = Placement; // 代表这个节点要被挂载到 dom上面 顺序变化了,说明这个节点被移动了,进行 dom 操作,tag 就是 Placement
            return lastPlacedIndex;
          } else {
            // This item can stay in place.
            return oldIndex; // 存在于原来的位置
          }
        } else {
          // current 为 null 说明节点没有被渲染过,它是一个插入的节点
          // 插入的节点也是需要使用 Placement 同样进行dom操作
          // 就是这个节点要根据某个顺序插入到 dom节点的后面
          // This is an insertion.
          newFiber.effectTag = Placement;
          return lastPlacedIndex;
        }
      }
      
    • 进入 mapRemainingChildren
      function mapRemainingChildren(
        returnFiber: Fiber,
        currentFirstChild: Fiber,
      ): Map<string | number, Fiber> {
        // Add the remaining children to a temporary map so that we can find them by
        // keys quickly. Implicit (null) keys get added to this set with their index
        // instead.
        // 通过 Map 对象找到 key相同的节点,判断是否可以复用
        const existingChildren: Map<string | number, Fiber> = new Map();
      
        let existingChild = currentFirstChild;
        // 遍历剩下的节点,获取其key 
        while (existingChild !== null) {
          if (existingChild.key !== null) {
            // set key value
            existingChildren.set(existingChild.key, existingChild);
          } else {
            // key 不存在,使用 index
            existingChildren.set(existingChild.index, existingChild);
          }
          existingChild = existingChild.sibling;
        }
        return existingChildren;
      }
      
    • 进入 updateFromMap
      function updateFromMap(
        existingChildren: Map<string | number, Fiber>,
        returnFiber: Fiber,
        newIdx: number,
        newChild: any,
        expirationTime: ExpirationTime,
      ): Fiber | null {
        // 匹配 文本节点
        if (typeof newChild === 'string' || typeof newChild === 'number') {
          // Text nodes don't have keys, so we neither have to check the old nor
          // new node for the key. If both are text nodes, they match.
          const matchedFiber = existingChildren.get(newIdx) || null; // 通过 newIdx 来查找,不管是否找到
          // 返回一个text node
          return updateTextNode(
            returnFiber,
            matchedFiber,
            '' + newChild,
            expirationTime,
          );
        }
        // 这里和 updateSlot类似
        if (typeof newChild === 'object' && newChild !== null) {
          switch (newChild.$$typeof) {
            case REACT_ELEMENT_TYPE: {
              const matchedFiber =
                existingChildren.get(
                  newChild.key === null ? newIdx : newChild.key,
                ) || null;
              if (newChild.type === REACT_FRAGMENT_TYPE) {
                return updateFragment(
                  returnFiber,
                  matchedFiber,
                  newChild.props.children,
                  expirationTime,
                  newChild.key,
                );
              }
              return updateElement(
                returnFiber,
                matchedFiber,
                newChild,
                expirationTime,
              );
            }
            case REACT_PORTAL_TYPE: {
              const matchedFiber =
                existingChildren.get(
                  newChild.key === null ? newIdx : newChild.key,
                ) || null;
              return updatePortal(
                returnFiber,
                matchedFiber,
                newChild,
                expirationTime,
              );
            }
          }
      
          if (isArray(newChild) || getIteratorFn(newChild)) {
            const matchedFiber = existingChildren.get(newIdx) || null;
            return updateFragment(
              returnFiber,
              matchedFiber,
              newChild,
              expirationTime,
              null,
            );
          }
      
          throwOnInvalidObjectType(returnFiber, newChild);
        }
      
        if (__DEV__) {
          if (typeof newChild === 'function') {
            warnOnFunctionType();
          }
        }
      
        return null;
      }
      

在 reconcileChildFibers 函数中调用的 reconcileChildrenIterator

定位到 packages/react-reconciler/src/ReactChildFiber.js#L891

function reconcileChildrenIterator(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  newChildrenIterable: Iterable<*>,
  expirationTime: ExpirationTime,
): Fiber | null {
  // This is the same implementation as reconcileChildrenArray(),
  // but using the iterator instead.
  
  // 获取 iteratorFn
  const iteratorFn = getIteratorFn(newChildrenIterable);
  invariant(
    typeof iteratorFn === 'function',
    'An object is not an iterable. This error is likely caused by a bug in ' +
      'React. Please file an issue.',
  );

  if (__DEV__) {
    // We don't support rendering Generators because it's a mutation.
    // See https://github.com/facebook/react/issues/12995
    if (
      typeof Symbol === 'function' &&
      // $FlowFixMe Flow doesn't know about toStringTag
      newChildrenIterable[Symbol.toStringTag] === 'Generator'
    ) {
      warning(
        didWarnAboutGenerators,
        'Using Generators as children is unsupported and will likely yield ' +
          'unexpected results because enumerating a generator mutates it. ' +
          'You may convert it to an array with `Array.from()` or the ' +
          '`[...spread]` operator before rendering. Keep in mind ' +
          'you might need to polyfill these features for older browsers.',
      );
      didWarnAboutGenerators = true;
    }

    // Warn about using Maps as children
    if ((newChildrenIterable: any).entries === iteratorFn) {
      warning(
        didWarnAboutMaps,
        'Using Maps as children is unsupported and will likely yield ' +
          'unexpected results. Convert it to a sequence/iterable of keyed ' +
          'ReactElements instead.',
      );
      didWarnAboutMaps = true;
    }

    // First, validate keys.
    // We'll get a different iterator later for the main pass.
    // 获取 newChildren,这里 newChildrenIterable 是具有 迭代特性的 children
    const newChildren = iteratorFn.call(newChildrenIterable);
    if (newChildren) {
      let knownKeys = null;
      let step = newChildren.next();
      for (; !step.done; step = newChildren.next()) {
        const child = step.value;
        knownKeys = warnOnInvalidKey(child, knownKeys);
      }
    }
  }

  const newChildren = iteratorFn.call(newChildrenIterable);
  invariant(newChildren != null, 'An iterable object provided no iterator.');

  // 声明很多变量
  let resultingFirstChild: Fiber | null = null;
  let previousNewFiber: Fiber | null = null;

  let oldFiber = currentFirstChild;
  let lastPlacedIndex = 0;
  let newIdx = 0;
  let nextOldFiber = null;
  
  // 通过 next 向下获取新节点
  let step = newChildren.next();
  // 这个 for 循环的判断是基于迭代器的,当遍历结束 .done 返回的是 false
  // step 都是 next,在 Fiber 中的key 如果不存在,使用 index
  // 这里也要模拟一个 index 出来
  for (
    ;
    oldFiber !== null && !step.done;
    newIdx++, step = newChildren.next()
  ) {
    if (oldFiber.index > newIdx) {
      nextOldFiber = oldFiber;
      oldFiber = null;
    } else {
      nextOldFiber = oldFiber.sibling;
    }
    const newFiber = updateSlot(
      returnFiber,
      oldFiber,
      step.value,
      expirationTime,
    );
    if (newFiber === null) {
      // TODO: This breaks on empty slots like null children. That's
      // unfortunate because it triggers the slow path all the time. We need
      // a better way to communicate whether this was a miss or null,
      // boolean, undefined, etc.
      if (!oldFiber) {
        oldFiber = nextOldFiber;
      }
      break;
    }
    if (shouldTrackSideEffects) {
      if (oldFiber && newFiber.alternate === null) {
        // We matched the slot, but we didn't reuse the existing fiber, so we
        // need to delete the existing child.
        deleteChild(returnFiber, oldFiber);
      }
    }
    lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
    if (previousNewFiber === null) {
      // TODO: Move out of the loop. This only happens for the first run.
      resultingFirstChild = newFiber;
    } else {
      // TODO: Defer siblings if we're not at the right index for this slot.
      // I.e. if we had null values before, then we want to defer this
      // for each null value. However, we also don't want to call updateSlot
      // with the previous one.
      previousNewFiber.sibling = newFiber;
    }
    previousNewFiber = newFiber;
    oldFiber = nextOldFiber;
  }

  if (step.done) {
    // We've reached the end of the new children. We can delete the rest.
    deleteRemainingChildren(returnFiber, oldFiber);
    return resultingFirstChild;
  }

  if (oldFiber === null) {
    // If we don't have any more existing children we can choose a fast path
    // since the rest will all be insertions.
    for (; !step.done; newIdx++, step = newChildren.next()) {
      const newFiber = createChild(returnFiber, step.value, expirationTime);
      if (newFiber === null) {
        continue;
      }
      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
      if (previousNewFiber === null) {
        // TODO: Move out of the loop. This only happens for the first run.
        resultingFirstChild = newFiber;
      } else {
        previousNewFiber.sibling = newFiber;
      }
      previousNewFiber = newFiber;
    }
    return resultingFirstChild;
  }

  // Add all children to a key map for quick lookups.
  const existingChildren = mapRemainingChildren(returnFiber, oldFiber);

  // Keep scanning and use the map to restore deleted items as moves.
  for (; !step.done; newIdx++, step = newChildren.next()) {
    const newFiber = updateFromMap(
      existingChildren,
      returnFiber,
      newIdx,
      step.value,
      expirationTime,
    );
    if (newFiber !== null) {
      if (shouldTrackSideEffects) {
        if (newFiber.alternate !== null) {
          // The new fiber is a work in progress, but if there exists a
          // current, that means that we reused the fiber. We need to delete
          // it from the child list so that we don't add it to the deletion
          // list.
          existingChildren.delete(
            newFiber.key === null ? newIdx : newFiber.key,
          );
        }
      }
      lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
      if (previousNewFiber === null) {
        resultingFirstChild = newFiber;
      } else {
        previousNewFiber.sibling = newFiber;
      }
      previousNewFiber = newFiber;
    }
  }

  if (shouldTrackSideEffects) {
    // Any existing children that weren't consumed above were deleted. We need
    // to add them to the deletion list.
    existingChildren.forEach(child => deleteChild(returnFiber, child));
  }

  return resultingFirstChild;
}
  • 这个 reconcileChildIterator 方法和上面的 reconcileChildrenArray 基本一致
  • 就是判断条件不同,不再赘述
  • 上述两个 API 是 对可遍历的 children 调和的一个过程
  • 目的是尽量的复用可复用的 fiber 节点
  • 减少对象声明和内存回收的过程
【React源码 - Fiber架构之Reconciler】 React源码-Fiber架构之Reconciler 阅读详情

相关推荐

cadence—virtuosoic617的使用技巧收集区

cadence使用

qq_33599939的博客 3万+

React16 diff全面讲解

本文较长,请耐心读完,相信我定有收货,如果有错误欢迎指正。 reconcileChildren 这个方法做的事情是对于刚创建的组件,会创建新的子 Fiber 节点,update 组件,将当前组件与该组件在上次更新时对应的 Fiber 节点比较(也就是俗称的 Diff 算法),将比较的结果生成新 Fiber 节点。 function reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime) { // 首.

susuzhe 2601

Unity ECS 之 Entitas 的简单介绍,插件下载、导入,和简单使用(一篇文入门搞懂ECS是什么)

Unity ECS 之 Entitas 的简单介绍,插件下载、导入,和简单使用(一篇文入门搞懂ECS是什么) 目录 Unity ECS 之 Entitas 的简单介绍,插件下载、导入,和简单使用(一篇文入门搞懂ECS是什么) 一、简单介绍 二、一些专有项简介 Entity Context Group Collector Matcher System Feature 三、注意实现 四、ECS 的下载 Github 上下载 Unity Asset Store 中下载 U...

仙魁XAN 7780

React 的源码与原理解读(六):reconcileChildren 与 DIFF 算法

本节的我们将从 上一节留下的问题出发,谈谈 **reconcileChildren()** 中怎么样最终生成 fiber 结点,其中我们会谈到 React 核心的 **DIFF 算法**,他的核心 —— 复用怎么实现,同时他是怎么样把比较的时间复杂度进行了优化

weixin_46463785的博客 1115

c++ mysql_init 返回无效指针_C++操作MYSQL遇到的一些问题

首先 我使用的是 vcpkg上面是我装的一些库《大大安利vcpkg 安装时一定要使用powershell进行编译装上vcpkg后装ligmariadb这个库,这个对Mysql操作还是挺方便的,然后说一下 在装库时碰到的一些问题1.网络问题1.参考了一下 github上的答案 将下载失败的url拷下来 自己下 然后再重新开始安装《vcpkg在安装时每一个需要下载的 都会提供url-仔细找,2.这个问...

weixin_32176627的博客 681

别再凭感觉了!手把手教你用Excel搞定激光器MPE与NOHD安全计算(附模板下载)

本文详细介绍了如何利用Excel自动化计算激光器的MPE(最大允许照射量)和NOHD(标称眼危害距离),提升激光安全评估的效率和准确性。通过实战案例和模板下载,帮助工程师快速掌握GB/T 7247标准中的关键计算,避免常见错误,适用于激光测距仪等设备的安评工作。

weixin_30954265的博客 468

React源码之diff算法 上篇 reconcileChildrenArray

本篇对照源码和jsx模版简单介绍React Fiber子节点的diff算法。源码中除了主要的三个for循环,placeChild是同样重要的细节,每一轮for都有一个placeChild,给每个Fiber打上插入(Placement)移动(Placement)删除(Deletion)标记。链接:https://juejin.cn/post/7503226971557806099。

W1192931158的博客 512

NE564锁相环FM解调电路设计:10.7MHz中频实测与3种失真优化方案

本文详细解析了NE564锁相环在10.7MHz中频FM解调电路中的设计与优化方案。通过分析锁相环技术基础、电路架构设计及典型失真问题,提供了3种有效的失真优化方法,包括限幅增强、VCO频偏补偿和环路滤波优化,显著提升解调性能。文章还涵盖了PCB布局要点和系统测试验证,为高频电子线路设计提供实用参考。

weixin_33314040的博客 350

reconcileChildren的解读

reconcileChildren dom diff的入口函数就是reconcileChildren,那么他的源码如下: //packages/react-reconciler/src/ReactFiberBeginWork.old.js export function reconcileChildren( current: Fiber | null,//当前的fiber节点 workInProgress: Fiber,// 新生成的fiber nextChildren: any,//

Maisule的博客 371

Godot 2D游戏开发实战:用GDScript复刻10款经典小游戏(附完整源码)

本文是一份Godot 2D游戏开发实战指南,通过使用GDScript语言复刻10款经典小游戏,帮助开发者快速掌握引擎核心功能。内容涵盖从环境配置、GDScript基础到贪吃蛇、Flappy Bird、打砖块、俄罗斯方块等项目的核心逻辑与代码实现,并附有完整源码,旨在通过实践提升独立开发能力。

gold的博客 779

【Network Management】AUTOSAR局部网络管理从0到1配置实践

AUTOSAR局部网络管理从0到1配置实践

qq_36056498的博客 2915

8.diff算法(妈妈再也不担心我的diff面试了)

人人都能读懂的react源码解析(大厂高薪必备) 8.diff算法(妈妈再也不担心我的diff面试了) 视频课程&调试demos ​ 视频课程的目的是为了快速掌握react源码运行的过程和react中的scheduler、reconciler、renderer、fiber等,并且详细debug源码和分析,过程更清晰。 ​ 视频课程:进入课程 ​ demos:demo 课程结构: 开篇(听说你还在艰难的啃react源码) react心智模型(来来来,让大脑有react思维吧) Fiber(我是在内存

bemystery的博客 275

EMC诊断技术及电磁兼容理论设计

本文深入探讨了EMC诊断技术,重点在于滤波篇,详细讲解了EMI滤波器的设计、选型、测试及应用注意事项。内容涵盖噪声限值法规、滤波器的法规依据、插入损耗测试以及滤波电容的高频特性。同时,还阐述了噪声源的近场与远场特性,如何快速定位和抑制噪声。通过实例分析,提供了滤波器件和辐射噪声抑制器件的选型策略。

让你爱上电路设计 2164

React16源码: React中的reconcileChildren的源码实现

在更新了一个节点之后,拿到它的props.children, 要根据这个children里面的 ReactElement 来去创建子树的所有的 fiber 对象, 要根据 props.children 来生成 fiber 子树,然后判断 fiber 对象它是否是可以复用的

Wang的专栏 2133

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

react 对象渲染_轻烤 React 核心机制 Reconciliation

Reconciliation 是 React 的 Diff 算法,也是 React 的 核心机制。本文将会来研究 Reconciliation,看看 Reconciliation 这个算法是怎么工作的。由于是 React 的核心机制,所以涉及到很多的概念和逻辑,烧烤哥也只能“轻轻地”烤一下,总结一些主要的原理,具体到源码层面的实现细节,还有待深入研究(而且一篇文章的篇幅肯定是说不清也说不...

weixin_29377233的博客 533

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

GPT-4稀疏激活原理:1.8万亿参数与2%每token的工程真相

大语言模型中的稀疏激活是一种通过动态选择子网络(如MoE专家)来降低计算开销的关键技术,其核心在于平衡模型容量、硬件约束与推理效率。它基于专家混合(MoE)架构,利用路由网络实现token级参数选择,使实际参与前向计算的参数远小于总参数量。这种机制显著缓解显存带宽压力,支持千亿级知识容量在有限GPU资源下部署,广泛应用于高并发API服务、边缘推理与模型量化落地场景。本文深入解析GPT-4所采用的1.8万亿参数规模与约2%每token激活率背后的架构设计、动态路由逻辑及INT4量化部署实践,揭示稀疏性并非固定

weixin_33860528的博客 1314

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

React源码揭秘(三):Diff算法详解

编者按:本文作者奇舞团前端开发工程师苏畅。代码参照React 16.13.1什么是Diff在前两篇文章中我们分别介绍了 React 的首屏渲染流程1和组件更新流程2,其中首屏渲染会渲染一...

奇舞周刊 1639

React的新引擎—React Fiber是什么?

这是一篇讲react Fiber算法的文章,深入浅出,并且作者自己实现了Fiber的核心代码,可以很好的帮助我们理解fiber 原文链接 另外,建议读这篇文章之前先看一下他的另外几篇关于react的文章,本篇是建立在其之上的 DIY React Didact Fiber: Incremental reconciliation github repository updated demo Why F...

weixin_33825683的博客 532

探索Virtual DOM的前世今生

作者:百度外卖 程亚杰 李显 卢培鹏 转载请标明出处 复制代码缘起 在前端开发过程中,对性能产生最大影响的因素莫过于DOM的重排重绘了,React作为前端框架领跑者,为了有效解决DOM更新开销的问题,采用了Virtual DOM的思路,不仅提升了DOM操作的效率,更推动了数据驱动式组件开发的形成与完善。一旦习惯了数据驱动式开发,再要求我们使用显式DOM操作开发的话,虐心程度无异于春节...

weixin_34259559的博客 267

一文读懂 React 组件渲染核心原理

这是我们团队杨劲松、杨杰强同学做的内部分享,文章从声明式渲染的基本原理开始,逐步深入讲解 React 渲染与节点挂载的基本流程与源码,适合初中阶同学阅读。PS:我们是字节游戏中台前端团队,...

杏仁技术站 3030

react div onclick叠加_小前端读源码 - React(浅析Keys原理)

在使用React的时候,我们经常无法避免使用循环去渲染元素。例如我们有一个商品列表,我们就需要根据后端提供的接口(一般是一个数组)循环渲染出商品信息。在渲染的商品组件中,如果不填写一个key给循坏渲染的组件,那么React将会提示一个警告。在React的官网文档中有说道,循坏渲染组件需要为组件添加一个兄弟组件之间唯一的key作为标识。列表 & Keys - React​react.docs...

weixin_39906358的博客 181

Deep In React 系列之详解 React 16 的 Diff 策略

阅读本文大约需要 9 分钟这是我 Deep In React 系列的第二篇文章,如果还没有读过的强烈建议你先读第一篇:详谈 React Fiber 架构(1)。前言我相信...

奇舞周刊 835

React diff 算法

前言 很久以前写过一篇了解虚拟DOM的文章,主要讲解了vue为什么会使用虚拟 DOM 以及 VUE 的 diff 算法。最近技术栈迁移到了 React,就好好研究了一下 React diff 算法的实现。 React Fiber 在了解 React diff 算法之前,先了解 React Fiber 相关的知识,将有助于后面对 diff 算法中比对的理解。React 16版本之后推出了 Fiber 的概念,React Fiber是对核心算法的一次重新实现,基本本文主要是讲 diff 算法,因此忽略分.

大灰狼的小绵羊哥哥的博客 610

向量数据库选型实战:从场景出发的11款主流方案深度解析

本文深度解析11款主流向量数据库的选型实战,从专业向量数据库如Milvus、Weaviate到传统数据库扩展如PgVector、Redis Stack,覆盖不同应用场景需求。通过性能实测数据和成本考量,帮助开发者根据数据规模、查询性能等维度选择最适合的向量数据库方案,提升AI应用效率。

weixin_30315723的博客 585

05【React再造之旅】从零实现一个React(下)

写在前面 本文继续上一节文章,来介绍下剩余的知识,如下: Render和Commit阶段 调和过程 函数组件 Hooks 我们接下来的部分就依次介绍下这些知识点。 代码获取 本文涉及到的代码全部上传至码云,需要的同学请在下面地址中获取: https://gitee.com/XuQianWen/zerocreate_react://gitee.com/XuQianWen/zerocreate_react ...

X北辰北的博客 813

200行代码实现简版react

现在(2018年)react在前端开发领域已经越来越

weixin_34254823的博客 310
上一篇: React16源码: React中的reconcileChildren的源码实现
下一篇: React16源码: React中的updateClassComponent的源码实现
Wang's Blog
博客等级 码龄12年 7434粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值