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

PID调参避坑指南:为什么你的电机总是震荡?从比例带积分到微分实战解析 本文针对电机控制中常见的震荡问题,提供了一份PID调参实战指南。文章深入解析了比例(P)、积分(I)、微分(D)三个参数对系统稳定性的影响,揭示了比例增益过大是导致震荡的关键原因之一,并给出了从纯比例调试到引入积分、微分的系统化调参流程与避坑要点,帮助工程师快速实现电机平稳、精准的控制。 阅读详情

completeUnitOfWork


1 )概述

  • 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段
  • 它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式
  • 在 performUnitOfWork 里面,还有一个方法叫做 completeUnitOfWork
  • 在 completeUnitOfWork 中会根据是否有中断,调用不同的一个处理方法
  • 什么叫中断呢?
    • 就是我们在渲染一个组件的时候,出现了错误的场景进行处理
    • 或者优先级被更高优先级打断,低优先级转移到下一次处理
  • 在 renderRoot 当中, 使用try catch去调用 workLoop 去循环每一个节点的
  • workLoop 里面调用的就是 performUnitOfWork
  • 在 performUnitOfWork 里面调用了 beginWork
  • 它里面就是执行各种不同组件的一个update的一个过程
  • 在后面 completeUnitOfWork ,是处理到一侧的子节点遍历到最下层的时候,它没有子节点可以返回了
  • 因为 beginWork 里面调用了各种update,它们都是return自己的child
  • 比如说,有这么一棵 fiber 树的一个情况的时候
  • 我们先去对 RootFiber 执行了 beginWork,然后执行了之后 return 的是它的 child,也就是 App
  • 一层一层这么执行下来之后,到了有分叉节点,也就是说一个节点,下面有多个子节点的情况
  • 它 return 的还是它的第一个 child 节点,所以这个时候,后面的节点是没有被执行到更新的
  • 因为 inpput 它返回了之后,返回的是它里面的 input child 这个节点
  • 到这里为止,这个 dom 的 input 节点,已经没有child了,这个时候就 return null
  • 因为它的 child是 null,所以在这里首先执行的是 beginWork
  • 如果有child,它就会一直往child方向进行一个查找
  • 等到 next 等于 null 的情况的时候,就会去执行 completeUnitOfWork
  • completeUnitOfWork 就是对当前这个input界定 执行它的complete,具体执行什么
  • 在 beginWork 的过程当中,如果我们去update某一个节点的时候,有报错了
  • 或者是 throw 了一个 promise, 就是 suspend 的情况, 它会执行一定的标记
  • 在 renderRoot 当中,我们可以看到它如果有报错,它的catch是要在 renderRoot 里面被捕获的这个错误
  • 对这个错误补获之后,它是有一定的处理的, 处理完之后,它仍然处于这个 while 循环当中,它里面并没有 break
    • 位置: packages/react-reconciler/src/ReactFiberScheduler.js#L1276
  • 这个时候它还是会往下进行一个执行的,只不过它会在里面给我们当前这个节点去执行一个标记
  • 在 completeUnitOfWork 的阶段,会对有各种不同标记的一个节点,执行不同的方法
  • 主要的是我们正常流程走的是叫 completeWork 这么一个方法
  • 而对于有异常的节点,它的一个流程是调用一个叫 unwindWork 的方法
  • 然后会判断是否有兄弟节点来执行不同的操作
    • 比如执行到上图 input 这个节点的 completeUnitOfWork之后,它没有兄弟节点
    • 它直接去执行它的父节点,也就是 Input 组件的 completeUnitOfWork
    • 在这里,我们发现它是有兄弟节点的,因为第一次update的过程,是往下的
    • 对于旁边的 List 节点是没有任何更新的, 这个时候我们就会返回它的兄弟节点 List
  • 对于这个兄弟节点, 继续对它执行一个update的一个过程,也就是 beginWork 的一个过程
  • 这就是在 completeUnitOfWork 里面非常重要的一个判断条件
    • 就是如果有兄弟节点,它会直接返回这个兄弟节点
  • 完成节点之后要赋值整个effect链
    • effect是非常重要的,后面即将要把所有的更新结束的节点,挂载到真正的dom上面
    • 这一个阶段叫做commit阶段,它要去执行每一个节点不同的跟dom有关的操作
    • 在前面 beginWork 和 completeUnitOfWork,对每一个节点的更新过程标记了
    • 有哪些 SideEffect 最终要被commit的一个过程
    • 在 completeUnitOfWork 里面会完成这一步,把所有的effect节点进行一个串联
    • 让 commitWork 的阶段可以非常方便的根据这个链去执行每一个节点的最终的操作

2 ) 源码

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L939
找到 completeUnitOfWork

// 这个API 是在 workInProgress 的基础上进行的一个操作
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
  // Attempt to complete the current unit of work, then move to the
  // next sibling. If there are no more siblings, return to the
  // parent fiber.

  // 进来首先就是一个 while true 的一个循环
  while (true) {
    // The current, flushed, state of this fiber is the alternate.
    // Ideally nothing should rely on this, but relying on it here
    // means that we don't need an additional field on the work in
    // progress.
    // 对于这个循环, 它首先获取current
    const current = workInProgress.alternate;
    if (__DEV__) {
      ReactCurrentFiber.setCurrentFiber(workInProgress);
    }
    // 然后 获取 returnFiber 和 siblingFiber,也就是它的父节点以及它的兄弟节点
    // 这个在后面我们判断是否要返回兄弟节点的时候就会用到
    const returnFiber = workInProgress.return;
    const siblingFiber = workInProgress.sibling;

    // 这里首先一进来就有一个大的判断,它是一个整个方法里面最大的一个判断
    // Incomplete 就是这个节点它是出现了错误,然后被捕获的, 并标记这个 sideEffect
    // 逻辑与操作来判断某一个属性上面它是否有某一个特性的一个方式
    if ((workInProgress.effectTag & Incomplete) === NoEffect) {
      if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
        // Don't replay if it fails during completion phase.
        mayReplayFailedUnitOfWork = false;
      }
      // This fiber completed.
      // Remember we're completing this unit so we can find a boundary if it fails.
      nextUnitOfWork = workInProgress;
      if (enableProfilerTimer) {
        if (workInProgress.mode & ProfileMode) {
          startProfilerTimer(workInProgress);
        }
        nextUnitOfWork = completeWork(
          current,
          workInProgress,
          nextRenderExpirationTime,
        );
        if (workInProgress.mode & ProfileMode) {
          // Update render duration assuming we didn't error.
          stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
        }
      } else {
        nextUnitOfWork = completeWork(
          current,
          workInProgress,
          nextRenderExpirationTime,
        );
      }
      if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
        // We're out of completion phase so replaying is fine now.
        mayReplayFailedUnitOfWork = true;
      }
      stopWorkTimer(workInProgress);
      resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
      if (__DEV__) {
        ReactCurrentFiber.resetCurrentFiber();
      }

      if (nextUnitOfWork !== null) {
        // Completing this fiber spawned new work. Work on that next.
        return nextUnitOfWork;
      }

      // 接下去这个个判断,是要构造一个所有 有 SideEffect 的节点的一个链状的结构
      // 这个链状结构最终是用于 commitWork 的时候用来进行对这些有 SideEffect的节点进行 commit 的一个操作,
      // 这边它的一个判断条件,returnFiber不等于null,并且returnFiber它不是一个 Incomplete 的一个节点
      // 因为对于一个 Incomplete 的节点,它唯一可以具有的一个SideEffect,就是这个节点已经被捕获了
      // 因为对于有 Incomplete 错误的节点是不会渲染正常的子节点的
      if (
        returnFiber !== null &&
        // Do not append effects to parents if a sibling failed to complete
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        // Append all the effects of the subtree and this fiber onto the effect
        // list of the parent. The completion order of the children affects the
        // side-effect order.
        // 对于正常的一个情况, 首先要判断一下 returnFiber.firstEffect 是否等于 null
        // 符合判断就代表 现在这个 returnFiber 上还没有记录任何它的子节点的有副作用的子节点
        // 这个时候, 直接把当前节点的firstEffect赋值给 returnFiber.firstEffect
        // 因为它之前是没有任何一个的嘛,我们这边真正要做的是把当前节点的 firstEffect 到 lastEffect的一个链条
        // 这个单项链表,给它挂载到它的父节点的同样的一个 firstEffect到lastEffect的单项链表的最后
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = workInProgress.firstEffect;
        }
        // 就是下面这一段判断,就是来做这个事情的, 如果returnFiber.lastEffect不等于null,那说明它已经有了
        // 那么对于returnFiber上面有记录过别的 SideEffect 的节点之后
        // 我们当前节点是挂载到整个 SideEffect 链的最后,就是下面这样,就是把它连到最后的上面
        if (workInProgress.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
          }
          // 还要操作如下,因为它的这个(returnFiber.lastEffect)指针目前还指向它原来的那个lastEffect
          // 在 赋值 nextEffect之后,它的最后一个就是这个链的最后一个已经变成 workInProgress.lastEffect,所以这边要执行这么一个操作
          // 当然这个条件是要建立在 workInProgress.lastEffect 是有值的情况
          // 这是把它们各自的firsteffect到lasteffect,这个链给它进行一个串联的过程
          returnFiber.lastEffect = workInProgress.lastEffect;
        }

        // If this fiber had side-effects, we append it AFTER the children's
        // side-effects. We can perform certain side-effects earlier if
        // needed, by doing multiple passes over the effect list. We don't want
        // to schedule our own side-effect on our own list because if end up
        // reusing children we'll schedule this effect onto itself since we're
        // at the end.
        // 对于returnFiber来说,当前这个节点也可能是有副作用的,那么这边就接下去就会做这个操作
        // 如果当前节点的 effectTag  > PerformedWork 的,因为 PerformedWork 是一个给 DEVTool 用的一个 sideEffect
        // 对于真正的react更新是没有任何意义的, 所以如果它仅仅只有 PerformedWork ,它就不是一个有效的 SideEffect 的节点
        const effectTag = workInProgress.effectTag;
        // Skip both NoWork and PerformedWork tags when creating the effect list.
        // PerformedWork effect is read by React DevTools but shouldn't be committed.
        if (effectTag > PerformedWork) {
          // 如果它有 SideEffect,就把当前节点作为父节点的 SideEffect 链的最后一个给它挂载上去
          // 或者如果是当前父节点没有任何记录的 SideEffect,它就是第一个
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress;
          } else {
            returnFiber.firstEffect = workInProgress;
          }
          returnFiber.lastEffect = workInProgress;
        }
      }

      if (__DEV__ && ReactFiberInstrumentation.debugTool) {
        ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
      }

      if (siblingFiber !== null) {
        // If there is more work to do in this returnFiber, do that next.
        return siblingFiber;
      } else if (returnFiber !== null) {
        // If there's no more work in this returnFiber. Complete the returnFiber.
        workInProgress = returnFiber;
        continue;
      } else {
        // We've reached the root.
        return null;
      }
    } else {
      if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
        // Record the render duration for the fiber that errored.
        stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);

        // Include the time spent working on failed children before continuing.
        let actualDuration = workInProgress.actualDuration;
        let child = workInProgress.child;
        while (child !== null) {
          actualDuration += child.actualDuration;
          child = child.sibling;
        }
        workInProgress.actualDuration = actualDuration;
      }

      // This fiber did not complete because something threw. Pop values off
      // the stack without entering the complete phase. If this is a boundary,
      // capture values if possible.
      const next = unwindWork(workInProgress, nextRenderExpirationTime);
      // Because this fiber did not complete, don't reset its expiration time.
      if (workInProgress.effectTag & DidCapture) {
        // Restarting an error boundary
        stopFailedWorkTimer(workInProgress);
      } else {
        stopWorkTimer(workInProgress);
      }

      if (__DEV__) {
        ReactCurrentFiber.resetCurrentFiber();
      }

      if (next !== null) {
        stopWorkTimer(workInProgress);
        if (__DEV__ && ReactFiberInstrumentation.debugTool) {
          ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
        }

        // If completing this work spawned new work, do that next. We'll come
        // back here again.
        // Since we're restarting, remove anything that is not a host effect
        // from the effect tag.
        next.effectTag &= HostEffectMask;
        return next;
      }

      if (returnFiber !== null) {
        // Mark the parent fiber as incomplete and clear its effect list.
        returnFiber.firstEffect = returnFiber.lastEffect = null;
        returnFiber.effectTag |= Incomplete;
      }

      if (__DEV__ && ReactFiberInstrumentation.debugTool) {
        ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
      }

      // 存在 sibling 节点, 注意这边是return,也就是跳出while循环
      if (siblingFiber !== null) {
        // If there is more work to do in this returnFiber, do that next.
        return siblingFiber;
      } else if (returnFiber !== null) {
        // If there's no more work in this returnFiber. Complete the returnFiber.
        // 如果returnFiber不等于null,那么 workInProgress = returnFiber
        // 比如对于最上面示例图的 input节点,它的 completeUnitOfWork
        // 如果它没有兄弟节点,那么它就继续执行Input的 completeUnitOfWork
        // 这个循环是在 completeUnitOfWork 内部进行的一个过程
        workInProgress = returnFiber;
        continue;
      } else {
        // 如果 returnFiber 也等于null,那么它直接 return null
        // 说明已经到达顶点了,就到达 RootFiber 了,我们的更新过程呢已经完成了
        // 只需要在接下去 commitRoot 就可以了
        return null;
      }
    }
  }

  // Without this explicit null return Flow complains of invalid return type
  // TODO Remove the above while(true) loop
  // eslint-disable-next-line no-unreachable
  return null;
}
  • 对于这个while循环里面,其实它的所有代码都是在if else里面的
  • if else它们最大的一个区别是什么呢?
    • 在if里面我们可以看这边这个特殊情况
    • 就是 nextUnitOfWork 等于 completeWork,然后传入一些属性
    • 对于 else 的情况,这边调用了 next = unwindWork
    • 这就是对于一个是否有抛出过错误的一个节点,执行的一个不同的操作
  • completeWork,是没有任何一个错误的一个节点,它的一个complete的一个过程
    • 调用了completework,就是把这个节点的更新给它完成了
  • unwindWork 是有节点的错误,被捕获,如何去处理的一个过程
  • 在上面的 SideEffect 到底有什么意义呢?
  • 比如有如下场景:
    • 有个列表上有值为 1, 2, 3的三个节点
    • 点击 button 按钮,会让上面三个节点内的值乘上自身
    • 对于2,3是4和9,对于1还是1
    • 这里 1 是一个没有变化的过程
    • 按照正常逻辑来说,只需要更新原本值是 2 和 3 的节点
    • 实际上 react 里面也是这样做的
  • 参考demo如下
    import React, { Component } from 'react'
    import './App.css'
    
    class List extends Component {
      state = {
        a: 1,
        b: 2,
        c: 3,
      }
    
      handleClick = () => {
        this.setState(oldState => {
          const { a, b, c } = oldState
          return {
            a: a * a,
            b: b * b,
            c: c * c,
          }
        })
      }
    
      render() {
        const { a, b, c } = this.state
        return [
          <span key="a">{a}</span>,
          <span key="b">{b}</span>,
          <span key="c">{c}</span>,
          <button key="button" onClick={this.handleClick}>
            click me
          </button>,
        ]
      }
    }
    
    class Input extends Component {
      state = {
        name: 'wang',
      }
    
      handleChange = e => {
        // 这里如果使用方法设置`state`
        // 那么需要现在外面读取`e.target.value`
        // 因为在React走完整个事件之后会重置event对象
        // 以复用event对象,如果等到方法被调用的时候再读取`e.target.value`
        // 那时`e.target`是`null`
        this.setState({
          name: e.target.value,
        })
      }
    
      render() {
        return (
          <input
            type="text"
            style={{ color: 'red' }}
            onChange={this.handleChange}
            value={this.state.name}
          />
        )
      }
    }
    
    class App extends Component {
      render() {
        return (
          <div className="main">
            <Input />
            <List />
          </div>
        )
      }
    }
    
    export default App
    
  • 这个时候我们在 浏览器中针对 react-dom.development.js 在 commitRoot 上面打了一个断点
  • 这个断点是看最终要进行commit的时候,它是如何去获取哪几个节点是要更新的
  • 也就是我们的 SideEffect 链是怎么样的一个形式, 点一下,断点被捕获,捕获了之后
  • 可以看到它接收的两个参数,一个是root,一个是finishedWork,那么它们其实是一个对应的关系
    • root.current.alternate 就等于 finishedWork
    • 因为 finishedWork 对应的是 workInProgress
    • finishedWork 上面就会记录我们当前这一次更新,它所有需要去执行的,就是要更新到dom上面的一些内容
      • 它的记录就在 firstEffect 到 lastEffect 的一个链上
      • 它这边 firstEffect 记录的 elementType 是一个 span
      • 所谓 effect 它最终是一个fiber节点,就是告诉我们哪一个fiber节点需要更新
      • 对于我们这边第二个span,它的值是要被更新成4的
      • 所以可以看到它的 firstEffect 的第一个节点是 span
      • 它的 effectTag是 4,然后它的 updateQueen 的属性里面是一个数组 updateQueue: ["children", "4"]
      • 这个大致意思可以看出,我们要把这个span标签对应的dom节点, 它的children里面显示的文字内容,从2变成4
      • 还有就是下一个节点, 也是一个span, 看 nextEffect, 它的 elementType 也是span
      • 它的 updateQueen 是 updateQueue: ["children", "9"],也就是原值为 3 的节点
  • 对于commitWork,只会关心 firstEffect 到 lastEffect 这个链条上面的对应的 fiber 节点
    • 它只需要更新的就是这两个dom节点的children的一个变化
  • 这就是react里面的 vdom,它以最小化的程度去更新 dom
  • 而不需要对其他的任何节点再进行一个操作,以此来提升一个性能
  • 这个过程, 就是在 v16.6.3/packages/react-reconciler/src/ReactFiberScheduler.js#L1026 这个 if 里面被实现的
  • 这个 demo 示例对应最最上面的流程图来说
    • 这边点了 button之后,在这里创建了一个update
    • 就是这个List上面, 通过调用 this.setState 去创建了这个 update
    • 最终是要从 RootFiber 上面往下进行一个更新,更新到 List 中的第一个span,以及后续的span
    • 然后发现只有第2和第3个span,它们的 children从 2 变成了 4,从 3 变成了 9
    • 它们两个对应的 SideEffect 是 Update 对应值为4 (二进制的表示形式, 参考 ReactSideEffectTags.js),它们要把内容挂载到dom上面
    • 第一个span的内容因为是1乘以1,所以它没有变化,就不需要执行真正的更新
    • 首先执行 completeUnitOfWork 的是 第一个 span, 接下去它的 sibling
    • 因为第一个 span 没有 SideEffect,在这里,它的 returnFiber 就是 List
    • 当前的 workInProgress 就是这第一个 span,这个span没有任何的 SideEffect
    • 这个 List 自己也没有 SideEffect,所以赋值List的时候,对List也是没有任何的一个更新的, 如下
      if (returnFiber.firstEffect === null) {
        returnFiber.firstEffect = workInProgress.firstEffect;
      }
      
    • 这时候的 List 本身的 firstEffect 到 lastEffect是没有任何内容的,也就是null
    • 进行到第2个span节点,它自己有 SideEffect, 而它没有子节点
    • 所以它本身上面的 firstEffect到lastEffect是没有任何内容的
    • 唯一需要操作的是要把它作为List的第一个 SideEffect 增加到这个链上面
    • 因为是 Update, 值为 4 肯定是大于 Performedwork 值为 1的
      if (effectTag > PerformedWork) {
        if (returnFiber.lastEffect !== null) {
          returnFiber.lastEffect.nextEffect = workInProgress; // 情况1,标记为 L1, 以便下面引用说明
        } else {
          returnFiber.firstEffect = workInProgress; // 情况2, 标记为 L2
        }
        returnFiber.lastEffect = workInProgress; // 通用设定3,标记为 L3
      }
      
    • 它自身(workInProgress)要作为 returnFiber 的第一个 SideEffect
    • 给它增加到 List 的firstEffect到lastEffect的链上
    • 所以returnFiber的 firstEffect 和 lastEffect,都等于这个Fiber对象(第二个span) workInProgress
    • 对应的执行语句是 L2 和 L3
    • 当第2个span执行complete完了,又要开始它的sibling, 也就是第三个span
    • 第三个span也是有更新的,所以它也要增加到 List上面
    • 这个时候List上面已经有变化,因为List.lastEffect(姑且这样表示)已经不等于null了
    • 这个时候,执行的是 else 的情况,即 L1
    • 因为在上一个节点的complete过程当中,已经指定了 firstEffect 和 lastEffect 都为 workInProgress(第2个span)
    • 所以第二个span节点的 nextEffect 指向的是第三个span的节点
    • 它们更新完之后又要更新 button,因为它没有任何的变化,所以 button 也没有更新
    • 这个时候对于List来说它的 SideEffect 链是等于 span指向span的这么一个过程
    • List的firstEffect指向第二个span对应的Fiber对象,lastEffect指向第三个span对应的Fiber对象
    • List本身执行 completeUnitOfWork 的时候,自己没有任何的更新
    • 因为它的state更新对于真正的dom操作,没有任何的关系,而且它也没有生命周期方法
    • 所以 List 后期没有任何要做的事情了, 自己本身是没有 SideEffect,只有子节点的 SideEffect
    • 在 List 执行 completeUnitOfWork 的时候, 代码中的 returnFiber 对应是 div
    • 而 workInProgress 对应的是 List, 而 List 要赋值它的 firstEffect 和 lastEffect
    • 它的链指向returnFiber, 就是要放到 returnFiber 它的链的最后面
    • returnFiber 这个 div,它目前是肯定没有任何的 SideEffect的, 所以它直接赋值成 workInProgress.firstEffect
    • 也就是说,对于 div 它的 firstEffect 跟 lastEffect 已经变成 List 的 firstEffect 跟 lastEffect
    • 因为上面节点都是没有任何更新的, 所以一层一层往上之后, 在 RootFiber 上面记录的
    • firstEffect 跟 lastEffect 也就变成了最开始的两个span
    • 所以这就是通过 completeUnitOfWork,最终能够赋值到 RootFiber 上面
    • 在它的 firstEffect 到 lastEffect,单项链表上面去记录整个应用当中
    • 所有需要去更新的最终的dom节点以及组件的fiber对象的一个过程
    • 这里涉及到后期 commitRoot 的时候, 调用这个单项链表一个过程,这里先跳过
  • 对于 unwindWork 的一个过程, 也是相似的, 先跳过 unwindWork 以及 completeWork
  • 目前只关心它的一个遍历过程,跳过具体节点做的事情, 关注它的大致流程
  • 它真正帮我们去实现了把所有需要更新的节点进行一个串联
  • 让最终 commitRoot 的时候,能够方便获取到所有需要更新的节点的一个过程
STM32F401RCT6最小系统原理图设计 STM32F401RCT6最小系统原理图设计一、主控芯片二、电源电路三、晶振电路三、下载电路四、完整电路图      本文章基于STM32F401RCT6进行最小系统原理图设计,包含主控芯片,电源,晶振电路,下载电路等几部分 一、主控芯片 主控芯片采用STM32F401RCT6,需注意: VBAT在不接电池的情况下需接3.3V,VDDAVREF+需接3.3V,否则会造成芯片供电不正常,无法启动和识别的情况 VDDAVREF+和VCAP1处电容需按照规格书 阅读详情

相关推荐

RealSense D455的标定并运行VINS-FUSION

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

qq_40186909的博客 2万+

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

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

杏仁技术站 3030

别再死记硬背了!用‘瞬时极性法’5分钟搞定三极管反馈电路类型判断

本文介绍了使用‘瞬时极性法’快速判断三极管反馈电路类型的高效技巧。通过假设输入信号变化并追踪传递路径,该方法能准确区分正负反馈、电压电流反馈、串联并联反馈及交直流反馈,避免传统死记硬背的弊端,显著提升电路分析效率。

weixin_42524708的博客 186

React原理之React整体渲染流程

上一篇这三个组件共同工作提供了 React 的高效和灵活的渲染机制,那么他们具体是怎么渲染的呢?React 的渲染流程分为两个阶段: - render 阶段:Reconciler 的工作阶段,这个阶段会调用组件的 render 方法 - commit 阶段:Renderer 的工作阶段,可以类比 git commit 提交,这个阶段会渲染具体的 UI。

jydchudq的博客 2740

基于 AUTOSAR 的域控产品软件开发进阶探索

本文深入探讨了AUTOSAR架构在汽车域控产品开发中的进阶应用。首先从系统设计角度分析了AUTOSAR分层架构原理及其在域控产品中的优化部署策略;其次详细介绍了Davinci和EBTresos等主流工具链的特点与协同使用方法;然后重点阐述了ASPICE流程与功能安全/信息安全标准的实施要点;最后分享了多核车载芯片(如NXP S32G)的适配经验与优化实践。通过架构设计、工具运用、标准遵循和芯片适配四个维度的专业解析,为域控产品的AUTOSAR开发提供了系统性指导。

SiArch的博客 1201

英伟达Orin芯片:自动驾驶的标杆产品

英伟达Orin芯片是面向自动驾驶和边缘AI的高性能SoC,采用7nm工艺,单芯片算力达254TOPS(INT8),功耗45-55W。其硬件架构包含12核ARM CPU、Ampere GPU、专用DLA和PVA加速器,支持64GB LPDDR5内存,符合车规级ASIL-D安全标准。软件生态涵盖DRIVE OS、CUDA、TensorRT等工具链,提供全栈解决方案。Orin已被理想、蔚来等车企采用,主要竞争对手包括高通Snapdragon Ride和地平线征程系列。面试需重点掌握算力指标、异构计算架构、Tens

学习代码 1828

React19源码系列之渲染阶段performUnitOfWork

react渲染阶段。

YanHSama的博客 1327

《QMT量化实战系列》支持自定义筛选与排序的多因子策略,实测年化收益超100% (1)

公众号推出《QMT量化实战》系列,通过多维度、多策略、多场景讲解QMT量化平台应用,每期附代码解读。本期复现《支持自定义筛选与排序的多因子策略》,策略包含三大核心模块:1. 通过Tushare获取30+财务因子数据;2. 提供灵活筛选机制(支持排除ST股、次新股及特定板块);3. 创新性多因子加权排序系统(支持自定义因子权重组合)。交易逻辑包含动态调仓机制,当持仓标的排名低于阈值时触发卖出。该策略具有高度可配置性,适合不同风险偏好的投资者。(149字)

qq_46262068的博客 1734

ReactDOM.render 是如何串联渲染链路的?(下)

以 completeWork为线索,去寻觅 Fiber树和 DOM 树之间的关联,将整个 render阶段理解透彻。在此基础上,结合 commit 阶段工作流,对 ReactDOM.render 所触发的渲染链路有一个完整、通透的理解。

从人到猿 638

【清华大学软院】15-19年推免试题历年整理

整理的题目为清软预推免机试(外校)硕士机考题目。 年代 题目1 题目2 题目3 2019 质因数分解 二叉搜索树 区间DP(折叠字符串) 2018 日期转换 高精度模拟 区间DP(戳气球) 2017 进制素数 被遗漏的数字 DP(收集苹果) 2016 大数运算 DP DFS 2015 进制素数: http://acm.hdu.edu.cn/showproblem.php?pid=1230 2017年题目(这个是找的最准确的题目): 1题见【链接】 2题见【链接】

云端筑梦师 2256

maxim max96712+max96717调试笔记

mxxim max96712 和max96717调试笔记

libuping2023的博客 1万+

React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)

renderRoot是一个函数,用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务,直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork,以及渲染新状态或 DOM。

警警的博客 1456

【前端圭臬】二:你知道的和不知道的 HTML

关于 HTML,说几个你可能不太关注的问题。

Evan 的博客 4759

React16源码: React中处理hydrate的核心流程源码实现

如果不能找到,就对这节点以及它的子树停止一个 hydrate 的流程,然后设置一些全局变量。在 beginWork 的流程当中,去判断更新的一个 HostComponent。是否可以从 hydrate 的 dom 节点当中去找到一个可以复用的节点。方法标志着整个 react 应用的 hydrate 过程的一个开启。第4个参数 forceHydrate 表示是否强制融合。接下去走的流程是在 completeUnitOfWork。开始了之后要做的是什么呢?

Wang的专栏 1584

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

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

在 `completeUnitOfWork` 当中,有一步比较重要的一个操作,就是重置`childExpirationTime`, childExpirationTime 是非常重要的一个时间节点,它用来记录某一个节点的子树当中,目前优先级最高的那个更新, 整个应用的调度过程当中使用的都是root节点,在 scheduleWork 的时候,即便我们创建更新的那个节点,是我们写的某一个组件

Wang的专栏 969

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

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

在 renderRoot 的 throw Exception 里面, 对于被捕获到错误的组件进行了一些处理 并且向上去寻找能够处理这些异常的组件,比如说 class component 里面具有 getDerivedStateFromError 或者 componentDidCatch 这样的生命周期方法 这个class component 就代表它可以处理它的子树当中渲染出来的任何的错误

Wang的专栏 1768

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

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

renderRoot 是一个非常复杂的方法, 它调用 workLoop 进行循环单元更新, 并且捕获错误并进行处理,最后,在走完流程之后要进行善后

Wang的专栏 1397

React16源码: React中context-stack的源码实现

如果在没有理清楚之前的整一个react的更新的过程的时候就来讲这些PI呢可能会让大家很难去理解这部分内容。context内部有一个概念是 stack, 有一个唯一的stack,里面保存各种各样的东西。react是一个非常纯粹的框架, 它的纯粹体现在哪里?所以呢我单独把它放到我们整个核心环节的后面。

Wang的专栏 1309

React 16 源码解析笔记 05 - render阶段03 workLoopSync 解析

workLoopSync 解析 上面已经把 workInProgress Fiber 树中的 rootFiber 构建完成,接下来要构建它的子级 fiber 对象了。 这个子级 fiber 对象就是 render 方法的第一个参数,它在代码走到 workLoopSync 方法时开始被构建。 workLoopSync 就是开启一个循环,以同步的方式开始构建 fiber 对象 workLoopSync workLoopSync 仅仅开启了一个 while 循环,用于构建 rootFiber 对象的所有子级 fi

皮蛋很白的博客 735

React 16.12.0 源码解读(四)之提交任务

React提交任务部分的源码梳理和解读。

Babyfaceqian的博客 384

React16源码: React中的completeWork中对不同类型节点处理的源码实现

在 `completeUnitOfWork` 当中,在节点是正常渲染没有任何出错的情况下, 会去调用 completework,对这个节点进行一个完成工作的一系列操作, 在update各种component的时候,执行了各种获取context相关的内容

Wang的专栏 725

React 16 源码解析笔记 06 - render阶段04 构建其余节点

构建其余节点 上面已经看完beginWork,是从父到子,构建子级节点的 fiber 对象,这是递归的 “递”阶段。 接着解析 completeUnitOfWork,这是从子到父,构建其余节点的 fiber 对象,是递归的 “归”阶段。 completeUnitOfWork不仅仅是构建其余节点的 fiber 对象,它在从子级到父级的过程中会经过每一个 fiber 节点对象,主要完成的事情包括: 构建其余节点的 fiber 对象 为每个 fiber 节点对象构建对应的真实 DOM 对象,并添加到 state

皮蛋很白的博客 291

React 源码 - Render Phase 的工作细节

在 Render 阶段对单个 Fiber 做好“更新准备”,并把它的“副作用”记下来:在这之上实现「深度优先后序遍历」(先子节点、再兄弟、再父节点),确保整棵 Fiber 树的每个节点都被处理到先“向下”调和,再“向上”归并,不碰 DOM,只收集副作用。

2401_85392853的博客 1218

4. render

文章目录4. renderrender 整体流程DFS 框架遍历关键处理方法源码分析前置知识点isWorkingnextRoot & nextRenderExpirationTimenextUnitOfWorkrenderRootbeginWorkbeginWorkbailoutOnAlreadyFinishedWorkHostRootFunctionComponentMemoComponentClassComponent创建更新processUpdateQueuefinishClassCompon

. 534

React源码阅读:虚拟DOM的初始化

前言 本文的主要目的是阅读源码的过程中做下笔记和分享给有需要的小伙伴,可能会有纰漏和错误,请读者自行判断,头一次写阅读代码的文章,可能写得有点乱,有什么问题欢迎一起探讨一起进步。 React的版本为16.4,主分支的代码,只贴出部分关键代码,完整代码请到Github查看。 React阅读系列文章 React源码阅读:概况 虚拟DOM的初始化 React.createElement 在阅读源码前,我...

weixin_34357962的博客 298

react源码debugger-各种fiber的completeWork阶段

原生div p标签等的bieginWork class DD extends Component { render() { return <div>123</div>; } } 上面调式了DD类组件的fiber,接着看类组件返回的子fiber,div标签的fiber是怎样的。 对于原生html的fiber,fiber.tag是5 export const HostComponent = 5; //原生标签 hostComponent的fiber { altern

lin_fightin的博客 415
上一篇: React16源码: React中的updateMemoComponent的源码实现
下一篇: React16源码: React中的resetChildExpirationTime的源码实现
Wang's Blog
博客等级 码龄12年 7438粉丝 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、付费专栏及课程。

余额充值