React16源码: React中commit阶段的commitRoot的主流程源码实现

ReactFiber在并发模式下的运行机制 ReactFiber在并发模式下的运行机制 新一年又开始了,React过去一年悄悄实现了Suspense,带来了React Hooks ,源码也大变换了。因此好有必须重新阅读一下,看它是如何实现这些功能,及未来准备开放的并发渲染功能。 React16早期将原来的diff过程拆分成两个阶段,第一个阶段叫reconcile, 也就是原来diff虚拟DOM的过程。这阶段有大量叫reconcile... 阅读详情

commitRoot


1 )概述

  • 在react中有 render 阶段 和 commit 阶段,这是两个不同的阶段
  • 1 )之前的渲染更新都是render阶段
  • 在render阶段,会经历一系列的调度,一系列的节点的更新过程
  • 需要去重新计算它的 state, props 生成新的fiber树和dom树
  • 在这个过程中,每一个节点的更新过程,它都是独立的
  • 每一个节点更新完之后,它都可以跳出这个更新的循环
  • 然后根据不同的更新模式可以被中断以及可以分片的更新
  • 能够让 react 把更多的优先级交还给浏览器
  • 让它可以及时的更新用户反馈,处理一些持续性的动画,让整体的页面会变得更流畅
  • 2 )进入到commit阶段,整个流程是不可以被中断的
  • react 尽量把更少的任务交给 commit 阶段
  • 所以,commit 阶段需要去更新的节点,会尽可能的控制在最少
  • 在之前的 renderRoot 结束的时候,会在 root 上面设置一个 finishedWork 这么一个对象
  • 这个对象是 RootFiber 对象, 这个对象包含了 firstEffect 到 lastEffect 单项链
  • 这个链就包含在整个 render 阶段,计算出来的需要在 commit 阶段进行操作的节点
  • 这些节点是在commit阶段根据赋值给它们的不同的 SideEffect 进行的不同的操作
  • commitRoot 这个方法
    • 包含很多去处理不同 SideEffect 的操作
    • 包含3个循环, 这3个循环都是对 firstEffect 到 lastEffect 单项链表上面
    • 每一个节点上面去更新内容,在更新完之后做的一些善后工作

2 )源码

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L586

定位到 commitRoot

function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
  // 一进来,它先设置了两个全局,变量是isworking和iscommitting
  // isworking,我们在 renderRoot 的过程当中也会设置为 true
  // 所以说 isWorking 这个全局变量,它就代表着我们正在进行更新的操作, 不管是它在哪一个阶段
  // isCommitting 就是单独的指明正在处于commit阶段,即提交的阶段
  isWorking = true;
  isCommitting = true;
  startCommitTimer();

  invariant(
    root.current !== finishedWork,
    'Cannot commit the same tree as before. This is probably a bug ' +
      'related to the return field. This error is likely caused by a bug ' +
      'in React. Please file an issue.',
  );
  
  // 进入之后,通过 root.pendingCommitExpirationTime,获取 committedExpirationTime
  // 这个 pendingCommitExpirationTime 就是在 onCommitRoot 的时候设置的
  // 在之前 render 阶段执行操作的 expirationTime
  const committedExpirationTime = root.pendingCommitExpirationTime;
  invariant(
    committedExpirationTime !== NoWork,
    'Cannot commit an incomplete root. This error is likely caused by a ' +
      'bug in React. Please file an issue.',
  );

  // 设置这个 root.pendingCommitExpirationTime 为 NoWork
  // 因为即将要对这个 committedExpirationTime 来进行一个commit的操作了
  // 接下去就是在执行完之后, root.pendingCommitExpirationTime 肯定就是已经没有了
  root.pendingCommitExpirationTime = NoWork;

  // Update the pending priority levels to account for the work that we are
  // about to commit. This needs to happen before calling the lifecycles, since
  // they may schedule additional updates.
  // 这边会做一个标记,就是 markCommittedPriorityLevels 这是标记我们的一个优先级
  // 它标记的结果是根据 finishedWork.expirationTime 以及 finishedWork.childExpirationTime
  // 因为 finishedWork 是一个 RootFiber,所以它的 childExpirationTime 代表着所有子树当中优先级最高的那一个任务
  // 而它的 expirationTime,大部分情况下都应该是跟它的 childExpirationTime 是相同的,但是会有一些情况不一样
  // 比如说通过外部强制指定的方式, 比如在有 nextRenderDidError 的情况下
  // 会设置它的 experiencetime 等于 Sync 这就是从外部去强制指定它的一个 expirationTime
  // 在这种情况下,它的 expirationTime 跟它的 childExpirationTime 会有比较大的区别
  // 当然并不是说没有这种外部指定,它们一定相同,大部分情况下,它们应该是相同的
  const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
  const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
  const earliestRemainingTimeBeforeCommit =
    childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit
      ? childExpirationTimeBeforeCommit
      : updateExpirationTimeBeforeCommit;
  markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);

  let prevInteractions: Set<Interaction> = (null: any);
  if (enableSchedulerTracing) {
    // Restore any pending interactions at this point,
    // So that cascading work triggered during the render phase will be accounted for.
    prevInteractions = __interactionsRef.current;
    __interactionsRef.current = root.memoizedInteractions;
  }

  // Reset this to null before calling lifecycles
  ReactCurrentOwner.current = null;

  // 它判断了 finishedWork,也就是我们的 RootFiber,它的 effectTag 是否大于 PerformedWork
  // 因为 PerformedWork 是给 DEV Tool 用的, 所以在整体的更新流程当中是没有意义的
  // 它大于 PerformedWork 代表它上面有 SideEffect,它也需要被 committed
  // 所以这边通过这么一个判断,如果是大于 PerformedWork 的了
  // 要把 finishedWork 的也作为一个 effect 来增加到它自己的 firstEffect 到 lastEffect 的这个链上面
  // 下面是增加的过程,因为要判断它目前上面是否有 SideEffect,以不同的方式插入到整个单链表的最后面
  let firstEffect;
  if (finishedWork.effectTag > PerformedWork) {
    // A fiber's effect list consists only of its children, not itself. So if
    // the root has an effect, we need to add it to the end of the list. The
    // resulting list is the set that would belong to the root's parent, if
    // it had one; that is, all the effects in the tree including the root.
    if (finishedWork.lastEffect !== null) {
      finishedWork.lastEffect.nextEffect = finishedWork;
      firstEffect = finishedWork.firstEffect;
    } else {
      firstEffect = finishedWork;
    }
  } else {
    // There is no effect on the root.
    firstEffect = finishedWork.firstEffect;
  }

  prepareForCommit(root.containerInfo);

  // Invoke instances of getSnapshotBeforeUpdate before mutation.
  nextEffect = firstEffect;
  startCommitSnapshotEffectsTimer();

  // 接下去就进入了三个循环:
  // 第一个循环主要是 commitBeforeMutationLifecycles,这个方法它其实很简单,它是这三个循环当中最简单的一个方法
  // 它唯一的作用就是调用 ClassComponent 上面可能会存在的 getSnapshotBeforeUpdate 这么一个生命周期方法
  while (nextEffect !== null) {
    let didError = false;
    let error;
    if (__DEV__) {
      invokeGuardedCallback(null, commitBeforeMutationLifecycles, null);
      if (hasCaughtError()) {
        didError = true;
        error = clearCaughtError();
      }
    } else {
      try {
        commitBeforeMutationLifecycles();
      } catch (e) {
        didError = true;
        error = e;
      }
    }
    if (didError) {
      invariant(
        nextEffect !== null,
        'Should have next effect. This error is likely caused by a bug ' +
          'in React. Please file an issue.',
      );
      captureCommitPhaseError(nextEffect, error);
      // Clean-up
      if (nextEffect !== null) {
        nextEffect = nextEffect.nextEffect;
      }
    }
  }
  stopCommitSnapshotEffectsTimer();

  if (enableProfilerTimer) {
    // Mark the current commit time to be shared by all Profilers in this batch.
    // This enables them to be grouped later.
    recordCommitTime();
  }

  // Commit all the side-effects within a tree. We'll do this in two passes.
  // The first pass performs all the host insertions, updates, deletions and
  // ref unmounts.
  nextEffect = firstEffect;
  startCommitHostEffectsTimer();

  // 第二个循环调用的方法是 commitAllHostEffects
  // 这个就是主要是操作对于 dom 节点它需要去做的一些内容
  // 比如说如果这个 dom 节点是刚刚新增的,那么要执行一个插入的操作
  // 如果这个 dom 节点它需要被删除,我们要执行删除的操作
  // 如果这个 dom 节点它的属性或者它 children 有更新,那么要执行一个更新的操作
  while (nextEffect !== null) {
    let didError = false;
    let error;
    if (__DEV__) {
      invokeGuardedCallback(null, commitAllHostEffects, null);
      if (hasCaughtError()) {
        didError = true;
        error = clearCaughtError();
      }
    } else {
      try {
        commitAllHostEffects();
      } catch (e) {
        didError = true;
        error = e;
      }
    }
    if (didError) {
      invariant(
        nextEffect !== null,
        'Should have next effect. This error is likely caused by a bug ' +
          'in React. Please file an issue.',
      );
      captureCommitPhaseError(nextEffect, error);
      // Clean-up
      if (nextEffect !== null) {
        nextEffect = nextEffect.nextEffect;
      }
    }
  }
  stopCommitHostEffectsTimer();

  resetAfterCommit(root.containerInfo);

  // The work-in-progress tree is now the current tree. This must come after
  // the first pass of the commit phase, so that the previous tree is still
  // current during componentWillUnmount, but before the second pass, so that
  // the finished work is current during componentDidMount/Update.
  root.current = finishedWork;

  // In the second pass we'll perform all life-cycles and ref callbacks.
  // Life-cycles happen as a separate pass so that all placements, updates,
  // and deletions in the entire tree have already been invoked.
  // This pass also triggers any renderer-specific initial effects.
  nextEffect = firstEffect;
  startCommitLifeCyclesTimer();

  // 接下去再做第三个方法叫做 commitAllLifeCycles
  // 这个就是跟组件还有各种各样的所有东西相关的生命周期的方法都会在这里面被调用
  while (nextEffect !== null) {
    let didError = false;
    let error;
    if (__DEV__) {
      invokeGuardedCallback(
        null,
        commitAllLifeCycles,
        null,
        root,
        committedExpirationTime,
      );
      if (hasCaughtError()) {
        didError = true;
        error = clearCaughtError();
      }
    } else {
      try {
        commitAllLifeCycles(root, committedExpirationTime);
      } catch (e) {
        didError = true;
        error = e;
      }
    }
    if (didError) {
      invariant(
        nextEffect !== null,
        'Should have next effect. This error is likely caused by a bug ' +
          'in React. Please file an issue.',
      );
      captureCommitPhaseError(nextEffect, error);
      if (nextEffect !== null) {
        nextEffect = nextEffect.nextEffect;
      }
    }
  }

  if (
    enableHooks &&
    firstEffect !== null &&
    rootWithPendingPassiveEffects !== null
  ) {
    // This commit included a passive effect. These do not need to fire until
    // after the next paint. Schedule an callback to fire them in an async
    // event. To ensure serial execution, the callback will be flushed early if
    // we enter rootWithPendingPassiveEffects commit phase before then.
    let callback = commitPassiveEffects.bind(null, root, firstEffect);
    if (enableSchedulerTracing) {
      // TODO: Avoid this extra callback by mutating the tracing ref directly,
      // like we do at the beginning of commitRoot. I've opted not to do that
      // here because that code is still in flux.
      callback = Schedule_tracing_wrap(callback);
    }
    passiveEffectCallbackHandle = Schedule_scheduleCallback(callback);
    passiveEffectCallback = callback;
  }

  // 以上3个循环中,三个方法调用完之后,我们的commit阶段就算已经完成了
  // 然后它会把这些全局变量设置为 false
  // 以及它会调用一个叫 onCommitRoot 的这么一个方法
  isCommitting = false;
  isWorking = false;
  stopCommitLifeCyclesTimer();
  stopCommitTimer();
  onCommitRoot(finishedWork.stateNode);
  if (__DEV__ && ReactFiberInstrumentation.debugTool) {
    ReactFiberInstrumentation.debugTool.onCommitWork(finishedWork);
  }

  // 它执行一段相关的 expirationTime 的判断, 它为什么要这边再做一次?
  // 因为我们在执行 ClassComponent 的生命周期方法的过程当中,可能又会产生新的更新
  // 产生新的更新的时候,RootFiber的 childExpirationTime 它又有可能会变化
  // 它不会把这一部分代码放到最前面去做,而是要放到最后面来做
  // 就是因为它需要等到我们如果在生命周期方法里面又创建了新的 update
  // 又产生了新的 childExpirationTime 的时候,那么再来进行这么一个判断
  // 这个判断其实主要做的事情,也就是设置一个全局变量
  // 叫做 legacyErrorBoundariesThatAlreadyFailed ,把它设置为 null
  const updateExpirationTimeAfterCommit = finishedWork.expirationTime;
  const childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
  const earliestRemainingTimeAfterCommit =
    childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit
      ? childExpirationTimeAfterCommit
      : updateExpirationTimeAfterCommit;
  if (earliestRemainingTimeAfterCommit === NoWork) {
    // If there's no remaining work, we can clear the set of already failed
    // error boundaries.
    legacyErrorBoundariesThatAlreadyFailed = null;
  }

  // 这里调用了一个方法叫做 onCommit
  // 这个 earliestRemainingTimeAfterCommit 是优先级较小,值较大的 expirationTime
  // 它就变成了新的root上面的 expirationTime
  onCommit(root, earliestRemainingTimeAfterCommit);

  // 忽略 polyfill 相关的
  if (enableSchedulerTracing) {
    __interactionsRef.current = prevInteractions;

    let subscriber;

    try {
      subscriber = __subscriberRef.current;
      if (subscriber !== null && root.memoizedInteractions.size > 0) {
        const threadID = computeThreadID(
          committedExpirationTime,
          root.interactionThreadID,
        );
        subscriber.onWorkStopped(root.memoizedInteractions, threadID);
      }
    } catch (error) {
      // It's not safe for commitRoot() to throw.
      // Store the error for now and we'll re-throw in finishRendering().
      if (!hasUnhandledError) {
        hasUnhandledError = true;
        unhandledError = error;
      }
    } finally {
      // Clear completed interactions from the pending Map.
      // Unless the render was suspended or cascading work was scheduled,
      // In which case– leave pending interactions until the subsequent render.
      const pendingInteractionMap = root.pendingInteractionMap;
      pendingInteractionMap.forEach(
        (scheduledInteractions, scheduledExpirationTime) => {
          // Only decrement the pending interaction count if we're done.
          // If there's still work at the current priority,
          // That indicates that we are waiting for suspense data.
          if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
            pendingInteractionMap.delete(scheduledExpirationTime);

            scheduledInteractions.forEach(interaction => {
              interaction.__count--;

              if (subscriber !== null && interaction.__count === 0) {
                try {
                  subscriber.onInteractionScheduledWorkCompleted(interaction);
                } catch (error) {
                  // It's not safe for commitRoot() to throw.
                  // Store the error for now and we'll re-throw in finishRendering().
                  if (!hasUnhandledError) {
                    hasUnhandledError = true;
                    unhandledError = error;
                  }
                }
              }
            });
          }
        },
      );
    }
  }
}
  • 进入 onCommit
    function onCommit(root, expirationTime) {
    	root.expirationTime = expirationTime;
      	root.finishedWork = null; // 因为 finishedWork 已经被 commit 掉了
    }
    
  • 以上就是整个 commitRoot的一个流程, 细节都写在代码注释中了
  • 这个流程当中,这三个循环看起来复杂,实际上很好阅读, 后续会看下相关的细节实现
Multisim 14.0元件库下载完整指南:从安装到配置 详细介绍Multisim 14.0版本的元件库下载方法,涵盖安装步骤与后续配置流程,帮助用户快速搭建仿真环境。内容覆盖常见问题及解决方案,让multisim元件库下载更高效、稳定。 阅读详情

相关推荐

合宙ESP32C3实战:MPU6500六轴传感器数据读取与校准全解析

本文详细解析了如何使用合宙ESP32C3开发板读取和校准MPU6500六轴姿态传感器数据。从硬件接线、开发环境搭建到核心代码解析,提供了全面的实战指南,帮助开发者快速掌握传感器配置与数据处理技巧,适用于姿态控制、运动检测等应用场景。

weixin_27066963的博客 407

React16源码: React中commit阶段的commitBeforeMutationLifecycles的源码实现

在 react commit阶段的 `commitRoot` 第一个while循环中, 调用了 `commitBeforeMutationLifeCycles`, 现在来看下,里面发生了什么

Wang的专栏 1124

【仿真】Carla Python API实战:从零搭建自动驾驶测试环境

本文详细介绍了如何使用Carla Python API从零搭建自动驾驶测试环境。通过Carla仿真平台,开发者可以构建高度逼真的城市环境和车辆物理模型,利用Python API实现传感器配置、数据采集、场景构建及自动驾驶算法集成。文章还分享了实际项目中的调试技巧和性能优化经验,帮助读者快速掌握自动驾驶仿真测试的核心技术。

weixin_29050513的博客 218

react源码解析10.commit阶段

react源码解析10.commit阶段 视频讲解(高效学习):进入学习 往期文章: 1.开篇介绍和面试题 2.react的设计理念 3.react源码架构 4.源码目录结构和调试 5.jsx&核心api 6.legacy和concurrent模式入口函数 7.Fiber架构 8.render阶段 9.diff算法 10.commit阶段 11.生命周期 12.状态更新流程 13.hooks源码 14.手写hooks 15.scheduler&Lane 16.concurrent模式 17.

weixin_59558923的博客 176

基于Dify与DeepSeek构建中医智能问答工作流:从知识库到精准处方

本文详细介绍了如何利用Dify与DeepSeek构建中医智能问答工作流,实现从知识库构建到精准处方的全流程。通过模型编排和知识检索技术,系统能够结合中医辨证思维与AI推理能力,为基层医师、学生和爱好者提供结构化诊疗建议。文章重点分享了知识库选材、症状标准化处理、智能检索与辨证推理等核心环节的实战经验。

weixin_29062255的博客 253

华为OD机考2025B卷 - 报文响应时间(Java & Python& JS & C++ & C )

华为OD机考2025B卷 - 报文响应时间

算法大师! 8083

React16源码: React中commit阶段的commitAllLifeCycles的源码实现

以上是对所有的具有 SideEffect 的节点在commit过程当中执行任务的一个过程,到这里,整个dom树其实也已经渲染上去了。包括可能存在 autofocus 的处理过程,所以它相当于是在整个应用渲染完成之后的一个善后工作。其实主要是调用不同组件的一个生命周期,以及对于 HostComponent,等组件的不同处理。具体细节,写在上述代码的注释中。

Wang的专栏 1442

特别章节-0.1 SolidWorks导出机械臂的URDF模型各个关节坐标系设置

通过Solidworks导出机械臂的URDF模型文件,建立机械臂的各个关节坐标系的规则以及注意点,详细步骤

六眼飞鱼的博客 1万+

React 技术揭秘:深入 useEffect 源码,理解 `flushPassiveEffects` 中销毁函数与回调函数的两阶段执行原理

本篇以 [docs/hooks/useeffect.md](https://link.gitcode.com/i/da20ce1a6ab3b628e981e59b1ba02bdc) 为主体,深入 `React` 内部 `flushPassiveEffects` 方法,剖析 `useEffect` 在 `commit` 阶段完成后的异步执行机制。你将掌握 `flushPassiveEffectsIm

gitblog_00154的博客 262

NVIDIA Isaac Sim 入门教程 - 运动生成控制器(Motion Generators)

机器人任务通常在任务空间中根据所需的末端执行器轨迹进行定义,而控制动作则在关节空间中执行。这自然会产生关节空间和任务空间(操作空间)控制方法。然而,要利用运动控制成功执行交互任务,往往需要对机械臂及其环境都建立精确的模型。虽然可以知道足够精确的机械臂模型,但很难获得详细的环境描述 [SSVO09]。可以通过在交互过程中引入顺应行为来克服这种不匹配造成的规划错误。虽然顺应性可以通过机器人的结构(如弹性致动器、软机械臂)被动实现,但我们对侧重于主动交互控制的控制器设计更感兴趣。这些设计大致分为注释。

2506_90492529的博客 1578

机器学习实战1-四种算法对比对客户信用卡还款情况进行预测与模型评估

大家好,我是微学AI,今天给大家带来一个机器学习实战案例:利用机器学习的四种算法对比对客户信用卡还款情况进行分类。信用卡又叫贷记卡,是由商业银行或信用卡公司对信用合格的消费者发行的信用证明。现在的年轻人,特别是80后,90后甚至00后到喜欢超前消费,每个人名下多多少少都有至少一张信用卡,有些人由于过度超前消费,导致下个月无法还款导致的逾期,这样会对个人征信产生影响,今天我们就来分析分析具有哪些特性的人会有信用卡逾期的可能。

微学AI的博客 4710

React源码总览:一张地图看懂Fiber、render与commit机制

前端框架的源码阅读是进阶的必经之路,但面对庞大的React仓库,许多人止步于复杂的模块结构。理解虚拟DOM、组件更新与渲染流程,需要先建立架构层面的全局认知。React作为主流UI库,其核心设计围绕Fiber架构展开,通过可中断的调度与双缓存机制,实现了从状态变更到界面更新的高效协调。掌握render阶段的计算与commit阶段的实际DOM操作,是理解React运行原理的关键。本文从包结构、核心模块、调用链三个层次,梳理一条清晰的学习路径,结合setState触发的完整旅程,帮助开发者快速定位源码入口,并建

weixin_29008055的博客 207

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

React 技术揭秘:React16 的三层架构(Scheduler-Reconciler-Renderer)如何支撑异步可中断更新

> 本文基于「React 技术揭秘」仓库 [docs/preparation/newConstructure.md](https://link.gitcode.com/i/2f46e28e79a56eaedddcb9e1b671ad21) 展开,并融合仓库内 [docs/preparation/oldConstructure.md](https://link.gitcode.com/i/52895

gitblog_00842的博客 366

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

just-react 并发模式指南:从入口函数到 Fiber 架构、Scheduler 与 lane 模型,理解 React Concurrent Mode 的底层实现

本文基于「React技术揭秘」仓库中 [docs/concurrent/prepare.md](https://link.gitcode.com/i/33780b2be1b49e99396ad961b1cd2c07) 一文展开。该文档是全书第八章《Concurrent Mode》的开篇总览,回答了三个核心问题:Concurrent 模式是什么、它为什么值得 React 花两年时间重构 Fiber

gitblog_01103的博客 890

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

前端两年经验面试大厂:从项目深挖到HR面的完整复盘

面试是对技术功底和工程实践的系统检验。前端开发者面对中大厂面试时,不仅需要掌握JavaScript核心原理(如事件循环、闭包),还要能深挖项目中的数据流与架构决策。从项目复盘到八股文实战,从React/Vue框架理解到算法与场景设计,每一步都考察候选人的问题解决能力和技术判断力。本文将结合两年经验的前端求职经历,拆解项目深挖、手写代码、场景设计、HR面谈薪等关键环节,提供一套可复用的备战方法论,帮助读者系统化地准备面试,展现真实的技术水平。

weixin_33850015的博客 439

Fix signatures do not match the previously installed version

报错信息FAILURE: Build failed with an exception.* What went wrong: Execution failed for task ':app:installDebug'. > com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Fai

Wang的专栏 2万+

在react中 实现 base64的图片转换成formdata格式并实现上传功能

如果不想要服务端实现处理base64格式的图片的转换,那么在浏览器端来实现,部分属于伪代码,仅作为开发参考,请勿直接拷贝使用

Wang的专栏 8836

快速解决gradle下载慢的问题的小技巧

在ionic run 或build 或clean命令的时候,如果没有下载过gradle, 需要重新下载一次,而通过CLI的方式下载很慢,即使科学上网后,下载还是存在一些问题。这里总结一下,快速的下载安装方式。 步骤 本地安装 http-server 包, 通过 $ npm i -g http-server 的方式安装到全局 在官网上下载自己的版本,目前自带的版本是 2.14.1,其他版本可能在执行过

Wang的专栏 6714

React 实现 2D 画板功能

在移动端或者PC端我们来模拟画板的实现, 支持清除和导出功能,此处简单实现,仅作开发参考。 效果演示 主要代码演示 /* 此模块用于实现签名绘图功能 */ // 需要用到的变量定义 let clickX = new Array(); let clickY = new Array(); let clickDrag = new Array(); let paint; let point...

Wang的专栏 4325

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

对于在 beginWork 中的 context 操作,可以看上述判断成立的条件下的代码,即便有符合跳过更新的操作,依然 需要 push 和 pop 操作。如果我们可以跳出组件的更新,也就是代表着当前这个 classComponent,它的state它的props都应该是没有任何变化的。对于子组件来说,它的所有子树所获取到的context肯定是经过子组件,和上层的父组件合并的 context 了, 也就是。所以,先 push 一个老的值进去再说, 然后到后面,如果发现这个组件它是要更新的,就调用这个。

Wang的专栏 2803
上一篇: React16源码: React中的unwindWork的源码实现
下一篇: React16源码: React中commit阶段的invokeGuardedCallback的源码实现
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、付费专栏及课程。

余额充值