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

news2024/11/18 8:26:42

renderRoot


1 )概述

  • renderRoot 是一个非常复杂的方法
  • 这个方法里处理很多各种各样的逻辑, 它主要的工作内容是什么?
  • A. 它调用 workLoop 进行循环单元更新
    • 遍历整个 Fiber Tree,把每一个组件或者 dom 节点对应的
    • Fiber 节点拿出来单一的进行更新,这是一个循环的操作
    • 把整棵 Fiber Tree 都遍历一遍,这就是 workLoop
  • B. 捕获错误并进行处理
    • 在进行每一个单元更新的时候,这个遍历逻辑的时候,有可能会出现一些错误
    • 有些是可预期的,比如说是 Suspense 的功能, throw 一个 Promise 对象
    • 这个时候,是我们要特殊的对它进行处理的
    • 有一些是不可预期的,比如说, render 里面报了一个错误
    • 我们也要对它进行一些处理,要告诉我们的react这个地方
    • 我们出现了一个错误,在react16之后,有了 Error Boundary
    • 可以在组件内捕获渲染的错误
  • C. 在走完流程之后要进行善后
    • 因为流程走完之后会有各种不同的情况
    • 比如说有错误的情况,比如说有任务被挂起的情况,也就是Suspense的情况
    • 这些任务,都要按照特定的逻辑给它进行一些处理
  • 这就是 renderRoot 这个方法,它的主要的核心工作内容

2 )流程图

  • 进入 renderRoot, 它里面有一个很核心的循环 do while workLoop
  • 这个while循环就是调用 workLoop 对整棵树,它每个节点进行一个遍历,并且拿出来单独进行更新
  • 因为每个 Fiber节点上,如果有更新的话,它会记入 updateQueen
  • 我们通过 updateQueen 上是否有内容来判断它是否要进行更新
  • 以及可以计算出它的新的 state,得到最新的 children,拿到所有最新的节点
  • 在 workLoop 的过程当中,它在做什么呢?
  • nextUnitOfWork 就是每一个节点在遍历的过程当中,它自己更新完之后,它会返回它的第一个child
  • 它的第一个child 就是作为 nextUnitOfWork,因为我们执行了一个节点的更新之后,我们需要返回
  • 返回之后,我们要判断一些逻辑
  • 比如,对于异步的操作,每个节点更新完之后都要判断 !shouldYield()
  • 判断我们现在的时间片是否还有?如果还有的话,再继续,如果没有的话,就要跳出了
  • 接下去就会执行 performUnitOfWork
  • 之后,执行 beginWork, completeUnitOfWork 这些,当然中间会判断是否有 next
  • next就是我在更新完一个结点之后,它是否还有下一个节点需要更新
  • 如果有next的情况,我们就返回,然后去判断这个逻辑是否还有
  • 这就是整个对整个 fiber tree 每个节点的遍历更新
  • 在这个更新过程当中,如果有任何catch,就是捕获到异常
  • 那么首先会进行一系列的判断,然后对它执行 throwException 或者是 onUncaughtError
  • 它们对应的逻辑会不一样, 如果这个节点它是可处理的错误
  • 我会直接对它进行 completeUnitOfWork() 因为更新到这个节点之后,它抛出错误了
  • 说明我们这个节点下面的所有子节点都不需要再更新了
  • 执行完成之后,我们就会调用 continue,对于这个 do while 循环,它又继续调用 workLoop
  • 也就是说我们把一棵子数的错误处理完之后,它还可以继续对别的子树进行更新
  • 整体更新完之后,就会 break,之后会有各种不同的情况,比如说有致命的错误等
  • 它们都会调用不同的逻辑进行一个处理,这里 nextRenderDidError 是一个可处理的错误
  • 比如说, 是可以被捕获的错误,有组件能捕获它,而 nextLatestAbsoluteTimeoutMs 是抛出promise的错误
  • 它是一个被挂起的一个任务,对应要执行一个被被挂起的操作, 最后如果上面的情况都没有出现
  • 直接 onComplete,之后就可以在root节点上 set finishedwork,这样, 就可以对它整体进行一个更新
  • 就可以执行 completeRoot,就可以把 fiber树 变成我们真正的dom 树去更新整个页面
  • 这就是整个 renderRoot 它的一个逻辑,简单总结
    • 先正常的执行每个单元的更新
    • 然后捕获到任何错误进行一定的处理
    • 最终把整个树遍历完之后根据不同的情况再进行一个处理

3 )源码

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

先看 renderRoot 的代码

function renderRoot(
  root: FiberRoot,
  isYieldy: boolean,
  isExpired: boolean,
): void {
  invariant(
    !isWorking,
    'renderRoot was called recursively. This error is likely caused ' +
      'by a bug in React. Please file an issue.',
  );
  isWorking = true;
  ReactCurrentOwner.currentDispatcher = Dispatcher;

  const expirationTime = root.nextExpirationTimeToWorkOn;

  // Check if we're starting from a fresh stack, or if we're resuming from
  // previously yielded work.
  // 刚进来的时候,进行这个判断
  // nextRoot 和 nextRenderExpirationTime 对应着接下来要渲染的节点和对应的过期时间 这是两个公共变量
  // 在这种情况下,说明调用这个方法的时候,接收到的参数和之前的不一样,可能就是之前的异步任务被新进来的高优先级的任务给打断了
  if (
    expirationTime !== nextRenderExpirationTime ||
    root !== nextRoot ||
    nextUnitOfWork === null
  ) {
    // Reset the stack and start working from the root.
    resetStack();
    nextRoot = root;
    nextRenderExpirationTime = expirationTime;
    // nextUnitOfWork 来自于 createWorkInProgress
    // 就是把当前的应用的状态对应的Fiber节点,拷贝了一份叫做 workInProgress 的对象
    // 因为我们不能直接在当前对象的Fiber节点上操作,它会影响我们目前的dom节点展示的样子
    // 所以要复制一份拷贝,对拷贝进行操作,workInProgress 和 current 之间会有一个转换的关系
    // 在renderRoot开始之后,我们真正操作的节点都是 workInProgress,没有直接在 current 上操作
    nextUnitOfWork = createWorkInProgress(
      nextRoot.current,
      null,
      nextRenderExpirationTime,
    );
    // 这种和不同expirationTime会有关系
    root.pendingCommitExpirationTime = NoWork;

    if (enableSchedulerTracing) {
      // Determine which interactions this batch of work currently includes,
      // So that we can accurately attribute time spent working on it,
      // And so that cascading work triggered during the render phase will be associated with it.
      const interactions: Set<Interaction> = new Set();
      root.pendingInteractionMap.forEach(
        (scheduledInteractions, scheduledExpirationTime) => {
          if (scheduledExpirationTime <= expirationTime) {
            scheduledInteractions.forEach(interaction =>
              interactions.add(interaction),
            );
          }
        },
      );

      // Store the current set of interactions on the FiberRoot for a few reasons:
      // We can re-use it in hot functions like renderRoot() without having to recalculate it.
      // We will also use it in commitWork() to pass to any Profiler onRender() hooks.
      // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
      root.memoizedInteractions = interactions;

      if (interactions.size > 0) {
        const subscriber = __subscriberRef.current;
        if (subscriber !== null) {
          const threadID = computeThreadID(
            expirationTime,
            root.interactionThreadID,
          );
          try {
            subscriber.onWorkStarted(interactions, threadID);
          } catch (error) {
            // Work thrown by an interaction tracing subscriber should be rethrown,
            // But only once it's safe (to avoid leaveing the scheduler in an invalid state).
            // Store the error for now and we'll re-throw in finishRendering().
            if (!hasUnhandledError) {
              hasUnhandledError = true;
              unhandledError = error;
            }
          }
        }
      }
    }
  }

  let prevInteractions: Set<Interaction> = (null: any);
  if (enableSchedulerTracing) {
    // We're about to start new traced work.
    // Restore pending interactions so cascading work triggered during the render phase will be accounted for.
    prevInteractions = __interactionsRef.current;
    __interactionsRef.current = root.memoizedInteractions;
  }

  let didFatal = false;

  startWorkLoopTimer(nextUnitOfWork);

  // 上面初始化工作做完之后,就开始 workLoop
  // 如果有 catch 就会有一大段处理逻辑, 这里先跳过
  // 
  do {
    try {
      workLoop(isYieldy);
    } catch (thrownValue) {
      if (nextUnitOfWork === null) {
        // This is a fatal error.
        didFatal = true;
        onUncaughtError(thrownValue);
      } else {
        if (__DEV__) {
          // Reset global debug state
          // We assume this is defined in DEV
          (resetCurrentlyProcessingQueue: any)();
        }

        const failedUnitOfWork: Fiber = nextUnitOfWork;
        if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
          replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);
        }

        // TODO: we already know this isn't true in some cases.
        // At least this shows a nicer error message until we figure out the cause.
        // https://github.com/facebook/react/issues/12449#issuecomment-386727431
        invariant(
          nextUnitOfWork !== null,
          'Failed to replay rendering after an error. This ' +
            'is likely caused by a bug in React. Please file an issue ' +
            'with a reproducing case to help us find it.',
        );

        const sourceFiber: Fiber = nextUnitOfWork;
        let returnFiber = sourceFiber.return;
        if (returnFiber === null) {
          // This is the root. The root could capture its own errors. However,
          // we don't know if it errors before or after we pushed the host
          // context. This information is needed to avoid a stack mismatch.
          // Because we're not sure, treat this as a fatal error. We could track
          // which phase it fails in, but doesn't seem worth it. At least
          // for now.
          didFatal = true;
          onUncaughtError(thrownValue);
        } else {
          throwException(
            root,
            returnFiber,
            sourceFiber,
            thrownValue,
            nextRenderExpirationTime,
          );
          nextUnitOfWork = completeUnitOfWork(sourceFiber);
          continue;
        }
      }
    }
    break;
  } while (true);

  if (enableSchedulerTracing) {
    // Traced work is done for now; restore the previous interactions.
    __interactionsRef.current = prevInteractions;
  }

  // We're done performing work. Time to clean up.
  isWorking = false;
  ReactCurrentOwner.currentDispatcher = null;
  resetContextDependences();

  // 在处理完 workLoop 后这里会有各种不同的判断
  // Yield back to main thread.
  // 这里代表有致命的错误
  if (didFatal) {
    const didCompleteRoot = false;
    stopWorkLoopTimer(interruptedBy, didCompleteRoot);
    interruptedBy = null;
    // There was a fatal error.
    if (__DEV__) {
      resetStackAfterFatalErrorInDev();
    }
    // `nextRoot` points to the in-progress root. A non-null value indicates
    // that we're in the middle of an async render. Set it to null to indicate
    // there's no more work to be done in the current batch.
    nextRoot = null;
    onFatal(root);
    return;
  }

  // 正常流程走完,这个if一定会匹配
  // 因为已经跳出 workLoop 了,说明一定有 react没有意识到的错误,所以调用 onYield
  if (nextUnitOfWork !== null) {
    // There's still remaining async work in this tree, but we ran out of time
    // in the current frame. Yield back to the renderer. Unless we're
    // interrupted by a higher priority update, we'll continue later from where
    // we left off.
    const didCompleteRoot = false;
    stopWorkLoopTimer(interruptedBy, didCompleteRoot);
    interruptedBy = null;
    onYield(root);
    return;
  }

  // We completed the whole tree.
  const didCompleteRoot = true;
  stopWorkLoopTimer(interruptedBy, didCompleteRoot);
  const rootWorkInProgress = root.current.alternate;
  invariant(
    rootWorkInProgress !== null,
    'Finished root should have a work-in-progress. This error is likely ' +
      'caused by a bug in React. Please file an issue.',
  );

  // `nextRoot` points to the in-progress root. A non-null value indicates
  // that we're in the middle of an async render. Set it to null to indicate
  // there's no more work to be done in the current batch.
  nextRoot = null;
  interruptedBy = null;

  // 这里也是
  if (nextRenderDidError) {
    // There was an error
    if (hasLowerPriorityWork(root, expirationTime)) {
      // There's lower priority work. If so, it may have the effect of fixing
      // the exception that was just thrown. Exit without committing. This is
      // similar to a suspend, but without a timeout because we're not waiting
      // for a promise to resolve. React will restart at the lower
      // priority level.
      markSuspendedPriorityLevel(root, expirationTime);
      const suspendedExpirationTime = expirationTime;
      const rootExpirationTime = root.expirationTime;
      onSuspend(
        root,
        rootWorkInProgress,
        suspendedExpirationTime,
        rootExpirationTime,
        -1, // Indicates no timeout
      );
      return;
    } else if (
      // There's no lower priority work, but we're rendering asynchronously.
      // Synchronsouly attempt to render the same level one more time. This is
      // similar to a suspend, but without a timeout because we're not waiting
      // for a promise to resolve.
      !root.didError &&
      !isExpired
    ) {
      root.didError = true;
      const suspendedExpirationTime = (root.nextExpirationTimeToWorkOn = expirationTime);
      const rootExpirationTime = (root.expirationTime = Sync);
      onSuspend(
        root,
        rootWorkInProgress,
        suspendedExpirationTime,
        rootExpirationTime,
        -1, // Indicates no timeout
      );
      return;
    }
  }

  // 注意这里的错误
  if (!isExpired && nextLatestAbsoluteTimeoutMs !== -1) {
    // The tree was suspended.
    const suspendedExpirationTime = expirationTime;
    markSuspendedPriorityLevel(root, suspendedExpirationTime);

    // Find the earliest uncommitted expiration time in the tree, including
    // work that is suspended. The timeout threshold cannot be longer than
    // the overall expiration.
    const earliestExpirationTime = findEarliestOutstandingPriorityLevel(
      root,
      expirationTime,
    );
    const earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
    if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
      nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
    }

    // Subtract the current time from the absolute timeout to get the number
    // of milliseconds until the timeout. In other words, convert an absolute
    // timestamp to a relative time. This is the value that is passed
    // to `setTimeout`.
    const currentTimeMs = expirationTimeToMs(requestCurrentTime());
    let msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
    msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;

    // TODO: Account for the Just Noticeable Difference

    const rootExpirationTime = root.expirationTime;
    onSuspend(
      root,
      rootWorkInProgress,
      suspendedExpirationTime,
      rootExpirationTime,
      msUntilTimeout,
    );
    return;
  }

  // Ready to commit.
  onComplete(root, rootWorkInProgress, expirationTime);
}
  • renderRoot 代码会相对比较长,要把代码的区块进行一个区分
  • 一些原版英文注释,和我添加的中文注释如上

现在来看下 workLoop 的源码

function workLoop(isYieldy) {
  if (!isYieldy) {
    // Flush work without yielding
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // Flush asynchronous work until the deadline runs out of time.
    while (nextUnitOfWork !== null && !shouldYield()) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  }
}
  • 它接收一个 isYieldy 作为参数
  • 这个参数意味着是否可以被中断
  • Sync的任务和已超时的异步任务都是不可中断的
  • 如果是不可中断的,只要有 nextUnitOfWork
  • 就会继续调用 performUnitOfWork
  • 如果是可以中断的,就通过判断 !shouldYield()
  • 来看当前时间片中是否还有足够的时间继续渲染下一个节点

再来看下 performUnitOfWork

function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  // 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.
  const current = workInProgress.alternate;

  // See if beginning this work spawns more work.
  startWorkTimer(workInProgress);
  if (__DEV__) {
    ReactCurrentFiber.setCurrentFiber(workInProgress);
  }

  if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
    stashedWorkInProgressProperties = assignFiberPropertiesInDEV(
      stashedWorkInProgressProperties,
      workInProgress,
    );
  }

  let next;
  if (enableProfilerTimer) {
    if (workInProgress.mode & ProfileMode) {
      startProfilerTimer(workInProgress);
    }

    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;

    if (workInProgress.mode & ProfileMode) {
      // Record the render duration assuming we didn't bailout (or error).
      stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
    }
  } else {
    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;
  }

  if (__DEV__) {
    ReactCurrentFiber.resetCurrentFiber();
    if (isReplayingFailedUnitOfWork) {
      // Currently replaying a failed unit of work. This should be unreachable,
      // because the render phase is meant to be idempotent, and it should
      // have thrown again. Since it didn't, rethrow the original error, so
      // React's internal stack is not misaligned.
      rethrowOriginalError();
    }
  }
  if (__DEV__ && ReactFiberInstrumentation.debugTool) {
    ReactFiberInstrumentation.debugTool.onBeginWork(workInProgress);
  }

  if (next === null) {
    // If this doesn't spawn new work, complete the current work.
    next = completeUnitOfWork(workInProgress);
  }

  ReactCurrentOwner.current = null;

  return next;
}
  • 它声明了一个 next 变量,next = beginWork(…), 这里涉及到对每个节点的更新
    • 更新完一个节点之后,它会返回它的下一个节点
    • 会更新 workInProgress.memoizedProps, 节点已经更新完了
    • 最新的 props 已经变成目前正在用的 props
    • 先跳过
  • 跳过 DEV 的代码
  • 如果 next === null 说明这个节点已经更新到子树的叶子节点了
    • 这棵子树就可以结束了
    • 结束就调用 completeUnitOfWork
    • 它也会返回它的下一个节点
  • 最后,return next
    • 在 workLoop 函数中可看到,它会赋值给 nextUnitOfWork
      // 参考其中一个 while
      while (nextUnitOfWork !== null) {
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
      }
      
    • 所以,真正到 nextUnitOfWork 为 null 的情况是它到了根节点,即 FiberRoot 节点
    • 它的 return 是 null,这时就跳出了 while 循环了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1399141.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

烟火检测AI边缘计算智能分析网关V4如何通过ssh进行服务器远程运维

智能分析网关V4是一款高性能、低功耗的AI边缘计算硬件设备&#xff0c;它采用了BM1684芯片&#xff0c;集成高性能8核ARM A53&#xff0c;主频高达2.3GHz&#xff0c;并且INT8峰值算力高达17.6Tops&#xff0c;FB32高精度算力达到2.2T&#xff0c;每个摄像头可同时配置3种算法&…

启动低轨道卫星LEO通讯产业与6G 3GPP NTN标准

通讯技术10年一个大跃进&#xff0c;从1990年的2G至2000年的3G网路&#xff0c;2010年的4G到近期2020年蓬勃发展的5G&#xff0c;当通讯技术迈入融合网路&#xff0c;当前的 5G 技术不仅可提供高频宽、低延迟&#xff0c;同时可针对企业与特殊需求以 5G 专网的模式提供各式服务…

vue-微信H5-拍照和视频,加人像框

图片拍照: <template><div><v-easy-camera:fullscreen"true"ref"easyCamera"v-model"pictureData.picture"class"main-camera"><template #header><div class"top"><van-imageclass"…

【跳槽面试】Redis中分布式锁的实现

分布式锁常见的三种实现方式&#xff1a; 数据库乐观锁&#xff1b;基于Redis的分布式锁&#xff1b;基于ZooKeeper的分布式锁。 本地面试考点是&#xff0c;你对Redis使用熟悉吗&#xff1f;Redis中是如何实现分布式锁的。 在Redis中&#xff0c;分布式锁的实现主要依赖于R…

【JavaEE Spring】SpringBoot 配置文件

SpringBoot 配置文件 1. 配置文件的作用1.1 配置文件的说明1.2 SpringBoot 配置文件 2. 配置文件的格式特殊说明 3. properties 配置文件说明3.1 properties 基本语法3.2 读取配置文件3.3 properties 缺点分析 4. yml 配置文件说明4.1 yml 的基本语法4.2 yml 使⽤进阶4.2.1 yml…

Java基础 - 07 Set之Set,AbstractSet

上边几篇&#xff0c;我们对java的List集合进行相关介绍&#xff0c;了解了关于List集合下的相关实现类的方法或者接口。 自本篇开始&#xff0c;将围绕java的Set进行介绍&#xff0c;也是对我java知识的巩固吧&#xff0c;处理业务越多&#xff0c;发现自己对基础知识的薄弱&…

数据结构小项目----通讯录的实现(这里用链表实现) 超详细~~~~૮(˶ᵔ ᵕ ᵔ˶)ა

目录 Contact.h说明&#xff1a; 结构体与头文件的包含&#xff1a; ​编辑 函数在头文件的声明与定义&#xff1a; Contact.c中各个函数的实现&#xff1a; 1.检查链表中的数据是否满了&#xff0c;满了就扩容 2.链表的尾插 3.链表的删除 4.查找名字是否匹配 5.初始化通讯…

Vagrant安装Oracle Data Guard环境示例

在Windows 11下&#xff0c;通过Vagrant安装标准的Data Guard环境&#xff08;默认为non-CDB模式&#xff09;&#xff0c;耗时约26分钟&#xff0c;共生成2台虚机。以下为安装日志&#xff1a; ...host2: Welcome to DGMGRL, type "help" for information.host2: C…

b+树的理解

二叉树&#xff1a; 每个节点支持两个分支的树结构&#xff0c;相比于单向链表&#xff0c;多了一个分支。 二叉查找树&#xff1a; 在二叉树的基础上增加了一个规则&#xff0c;左子树的所有节点都小于它的根节点&#xff0c;右子树的所有节点都大于他的根节点。 二叉查找树…

Beego之Beego快速入门

1、beego快速入门 1.1 新建项目 新建一个项目&#xff1a; [rootzsx src]# bee new quickstart 2023/02/19 15:55:50.370 [D] init global config instance failed. If you do not use this, just ignore it. open conf/app.conf: no such file or directory 2023/02/19 1…

易优demo网站测试结果

易优demo网站测试结果-06 1、信息收集 网站账号&#xff1a;admin 密码&#xff1a;Aa123456 2、存在的漏洞 2.1 后台弱口令漏洞 http://eyoucms-s347fqn.gxalabs.com/login.php?sAdmin/login网站账号&#xff1a;admin 密码&#xff1a;Aa123456 成功登陆 2.2 代码远程…

递归、搜索与回溯算法(专题二:深搜)

往期文章&#xff08;希望小伙伴们在看这篇文章之前&#xff0c;看一下往期文章&#xff09; &#xff08;1&#xff09;递归、搜索与回溯算法&#xff08;专题零&#xff1a;解释回溯算法中涉及到的名词&#xff09;【回溯算法入门必看】-CSDN博客 &#xff08;2&#xff09…

linux基础学习(5):yum

yum是为了解决rpm包安装依赖性而产生的一种安装工具 1.yum源 1.1配置文件位置 yum源的配置文件在/etc/yum.repos.d/中 *Base源是网络yum源&#xff0c;也就是需要联网才能使用的yum源。默认情况下&#xff0c;系统会使用Base源 *Media源是光盘yum源&#xff0c;是本地yum源…

openjdk源码了解

openjdk给出debug配置选项&#xff0c;common/autoconf/jdk-options.m4 AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_LEVEL], [################################################################################# Set the debug level# release: no debug information, all opti…

Jenkins实现CICD(1)_Windows10 安装Jenkins

文章目录 一、打开Jenkins官网&#xff0c;下载安装包二、安装Jenkins三、JAVA环境_JDK17下载安装&#xff08;Windows版&#xff09;四、将jdk-17添加到系统环境变量五、jenkins关联jdk-17六、安装常用插件(例如&#xff1a;git、gitlab、钉钉) 一、打开Jenkins官网&#xff0…

PDshell16逆向PostgreSQL 工程显示字段comment备注

现状&#xff1a;当刚逆向成功的表结构是没有原来表结构中的&#xff0c;comment备注如下 然后pd逆向工程的sql已经返回了这个备注的含义 解决方案&#xff1a; 1、设置显示注释列 tools——Display Preferences…如下 勾选-按照下面得方式勾选这三个 复制这里的VBS脚本&a…

Python数据分析案例37——基于分位数神经网络(QRNN)的汇率预测

案例背景 我导师的研究方向是少有的做"分位数回归"方向&#xff0c;作为研究机器学习深度学习方向的我自然就继承了这个特色&#xff0c;改进出了很多特殊结合方法&#xff0c;我会结合各种机器学习方法和各种分位数回归的方法。 之前写过分位数随机森林&#xff0…

哈希表 -- 刷题(查找算法)

目录 &#x1f4bb;哈希 -- 知识点 &#x1f40d;刷题 &#x1f33c;1&#xff0c;雪花 AC -- vector AC -- 链式前向星 &#x1f33c;2&#xff0c;公式 &#x1f4bb;哈希 -- 知识点 线性表 和 树表&#xff0c;通过比较关键字进行查找 而 散列表&#xff0c;基于…

【MATLAB源码-第119期】基于matlab的GMSK系统1bit差分解调误码率曲线仿真,输出各个节点的波形以及功率谱。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 GMSK&#xff08;高斯最小频移键控&#xff09;是一种数字调制技术&#xff0c;广泛应用于移动通信&#xff0c;例如GSM网络。它是一种连续相位调频制式&#xff0c;通过改变载波的相位来传输数据。GMSK的关键特点是其频谱的…

【接上篇】二、Flask学习之CSS(下篇)

上篇&#xff1a;二、Flask学习之CSS 3.8hover hover是用来美化鼠标悬停的效果的&#xff0c;当鼠标停放在某个区域&#xff0c;就会执行对应的hover操作。可以操作本标签的内容&#xff0c;也可以操作本标签下某一个标签的内容 3.9after <!DOCTYPE html> <html l…