React源码分析2-深入理解fiber

news2025/2/27 2:05:50

react16 版本之后引入了 fiber,整个架构层面的 调度、协调、diff 算法以及渲染等都与 fiber 密切相关。所以为了更好地讲解后面的内容,需要对 fiber 有个比较清晰的认知。本章将介绍以下内容:

  • 为什么需要 fiber
  • fiber 节点结构中的属性
  • fiber 树是如何构建与更新的

为什么需要 fiber

Lin Clark 在 React Conf 2017 的演讲中,他通过漫画的形式,很好地讲述了 fiber 为何出现,下面我根据她的演讲,结合我自己的理解来谈一谈 fiber 出现的原因。

fiber 之前

在 react15 及之前 fiber 未出现时,react 的一系列执行过程例如生命周期执行、虚拟 dom 的比较、dom 树的更新等都是同步的,一旦开始执行就不会中断,直到所有的工作流程全部结束为止。

要知道,react 所有的状态更新,都是从根组件开始的,当应用组件树比较庞大时,一旦状态开始变更,组件树层层递归开始更新,js 主线程就不得不停止其他工作。例如组件树一共有 1000 个组件需要更新,每个组件更新所需要的时间为 1s,那么在这 1s 内浏览器都无法做其他的事情,用户的点击输入等交互事件、页面动画等都不会得到响应,体验就会非常的差。

这种情况下,函数堆栈的调用就像下图一样,层级很深,很长时间不会返回

fiber 之后

为了解决这一问题,react 引入了 fiber 这种数据结构,将更新渲染耗时长的大任务,分为许多的小片。每个小片的任务执行完成后,都先去执行其他高优先级的任务(例如用户点击输入事件、动画等),这样 js 的主线程就不会被 react 独占,虽然任务执行的总时间不变,但是页面能够及时响应高优先级任务,显得不会卡顿了。

fiber 分片模式下,浏览器主线程能够定期被释放,保证了渲染的帧率,函数的堆栈调用如下(波谷表示执行分片任务,波峰表示执行其他高优先级任务): react 通过 fiber,为我们提供了一种跟踪、调度、暂停和中止工作的便捷方式,保证了页面的性能和流畅度。

fiber 节点结构

fiber 是一种数据结构,每个 fiber 节点的内部,都保存了 dom 相关信息、fiber 树相关的引用、要更新时的副作用等,我们可以看一下源码中的 fiber 结构:

相关参考视频讲解:进入学习

// packages/react-reconciler/src/ReactInternalTypes.js

export type Fiber = {|
  // 作为静态数据结构,存储节点 dom 相关信息
  tag: WorkTag, // 组件的类型,取决于 react 的元素类型
  key: null | string,
  elementType: any, // 元素类型
  type: any, // 定义与此fiber关联的功能或类。对于组件,它指向构造函数;对于DOM元素,它指定HTML tag
  stateNode: any, // 真实 dom 节点

  // fiber 链表树相关
  return: Fiber | null, // 父 fiber
  child: Fiber | null, // 第一个子 fiber
  sibling: Fiber | null, // 下一个兄弟 fiber
  index: number, // 在父 fiber 下面的子 fiber 中的下标

  ref:
    | null
    | (((handle: mixed) => void) & {_stringRef: ?string, ...})
    | RefObject,

  // 工作单元,用于计算 state 和 props 渲染
  pendingProps: any, // 本次渲染需要使用的 props
  memoizedProps: any, // 上次渲染使用的 props
  updateQueue: mixed, // 用于状态更新、回调函数、DOM更新的队列
  memoizedState: any, // 上次渲染后的 state 状态
  dependencies: Dependencies | null, // contexts、events 等依赖

  mode: TypeOfMode,

  // 副作用相关
  flags: Flags, // 记录更新时当前 fiber 的副作用(删除、更新、替换等)状态
  subtreeFlags: Flags, // 当前子树的副作用状态
  deletions: Array<Fiber> | null, // 要删除的子 fiber
  nextEffect: Fiber | null, // 下一个有副作用的 fiber
  firstEffect: Fiber | null, // 指向第一个有副作用的 fiber
  lastEffect: Fiber | null, // 指向最后一个有副作用的 fiber 

  // 优先级相关
  lanes: Lanes,
  childLanes: Lanes,

  alternate: Fiber | null, // 指向 workInProgress fiber 树中对应的节点

  actualDuration?: number,
  actualStartTime?: number,
  selfBaseDuration?: number,
  treeBaseDuration?: number,
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
  _debugNeedsRemount?: boolean,
  _debugHookTypes?: Array<HookType> | null,
|};

dom 相关属性

fiber 中和 dom 节点相关的信息主要关注 tagkeytypestateNode

tag

fiber 中 tag 属性的 ts 类型为 workType,用于标记不同的 react 组件类型,我们可以看一下源码中 workType 的枚举值:

// packages/react-reconciler/src/ReactWorkTags.js

export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
export const OffscreenComponent = 23;
export const LegacyHiddenComponent = 24;

在 react 协调时,beginWork 和 completeWork 等流程时,都会根据 tag 类型的不同,去执行不同的函数处理 fiber 节点。

key 和 type

keytype 两项用于 react diff 过程中确定 fiber 是否可以复用。

key 为用户定义的唯一值。type 定义与此fiber关联的功能或类。对于组件,它指向函数或者类本身;对于DOM元素,它指定HTML tag。

stateNode

stateNode 用于记录当前 fiber 所对应的真实 dom 节点或者当前虚拟组件的实例,这么做的原因第一是为了实现 Ref ,第二是为了实现真实 dom 的跟踪。

链表树相关属性

我们看一下和 fiber 链表树构建相关的 returnchildsibling 几个字段:

  • return:指向父 fiber,若没有父 fiber 则为 null
  • child: 指向第一个子 fiber,若没有任何子 fiber 则为 null
  • sibling:指向下一个兄弟 fiber,若没有下一个兄弟 fiber 则为 null
    通过这几个字段,各个 fiber 节点构成了 fiber 链表树结构:

副作用相关属性

首先理解一下 react 中的副作用,举一个生活中比较通俗的例子:我们感冒了本来吃点药就没事了,但是吃了药发现身体过敏了,而这个“过敏”就是副作用。react 中,我们修改了 state、props、ref 等数据,除了数据改变之外,还会引起 dom 的变化,这种 render 阶段不能完成的工作,我们称之为副作用。

flags

react 中通过 flags 记录每个节点 diff 后需要变更的状态,例如 dom 的添加、替换、删除等等。我们可以看一下源码中 Flags 枚举类型:

例如 Deletion 代表更新时要对 dom 进行删除,Placement 代表要进行添加或者替换等等。

// packages/react-reconciler/src/ReactFiberFlags.js

export type Flags = number;

export const NoFlags = /*                      */ 0b000000000000000000;
export const PerformedWork = /*                */ 0b000000000000000001;
export const Placement = /*                    */ 0b000000000000000010;
export const Update = /*                       */ 0b000000000000000100;
export const PlacementAndUpdate = /*           */ 0b000000000000000110;
export const Deletion = /*                     */ 0b000000000000001000;
export const ContentReset = /*                 */ 0b000000000000010000;
export const Callback = /*                     */ 0b000000000000100000;
export const DidCapture = /*                   */ 0b000000000001000000;
export const Ref = /*                          */ 0b000000000010000000;
export const Snapshot = /*                     */ 0b000000000100000000;
export const Passive = /*                      */ 0b000000001000000000;
export const PassiveUnmountPendingDev = /*     */ 0b000010000000000000;
export const Hydrating = /*                    */ 0b000000010000000000;
export const HydratingAndUpdate = /*           */ 0b000000010000000100;
export const LifecycleEffectMask = /*          */ 0b000000001110100100;
export const HostEffectMask = /*               */ 0b000000011111111111;
export const Incomplete = /*                   */ 0b000000100000000000;
export const ShouldCapture = /*                */ 0b000001000000000000;
export const ForceUpdateForLegacySuspense = /* */ 0b000100000000000000;
export const PassiveStatic = /*                */ 0b001000000000000000;
export const BeforeMutationMask = /*           */ 0b000000001100001010;
export const MutationMask = /*                 */ 0b000000010010011110;
export const LayoutMask = /*                   */ 0b000000000010100100;
export const PassiveMask = /*                  */ 0b000000001000001000;
export const StaticMask = /*                   */ 0b001000000000000000;
export const MountLayoutDev = /*               */ 0b010000000000000000;
export const MountPassiveDev = /*              */ 0b100000000000000000;

Effect List

在 render 阶段时,react 会采用深度优先遍历,对 fiber 树进行遍历,把每一个有副作用的 fiber 筛选出来,最后构建生成一个只带副作用的 Effect list 链表。和该链表相关的字段有 firstEffectnextEffectlastEffect

firstEffect 指向第一个有副作用的 fiber 节点,lastEffect 指向最后一个有副作用的节点,中间的节点全部通过 nextEffect 链接,最终形成 Effect 链表。

在 commit 阶段,React 拿到 Effect list 链表中的数据后,根据每一个 fiber 节点的 flags 类型,对相应的 DOM 进行更改。

其他

其他需要重点关注一下的属性还有 lanealternate

lane

lane 代表 react 要执行的 fiber 任务的优先级,通过这个字段,render 阶段 react 确定应该优先将哪些任务提交到 commit 阶段去执行。

我们看一下源码中 lane 的枚举值:

// packages/react-reconciler/src/ReactFiberLane.js

InputDiscreteHydrationLane: Lane = /*                   */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;
const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;
const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;
export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;
export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;
const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

同 Flags 的枚举值一样,Lanes 也是用 31 位的二进制数表示,表示了 31 条赛道,位数越小的赛道,代表的优先级越高。

例如 InputDiscreteHydrationLaneInputDiscreteLanesInputContinuousHydrationLane 等用户交互引起的更新的优先级较高,DefaultLanes 这种请求数据引起更新的优先级中等,而 OffscreenLaneIdleLanes 这种优先级较低。

优先级越低的任务,在 render 阶段越容易被打断,commit 执行的时机越靠后。

alternate

当 react 的状态发生更新时,当前页面所对应的 fiber 树称为 current Fiber,同时 react 会根据新的状态构建一颗新的 fiber 树,称为 workInProgress Fiber。current Fiber 中每个 fiber 节点通过 alternate 字段,指向 workInProgress Fiber 中对应的 fiber 节点。同样 workInProgress Fiber 中的 fiber
节点的 alternate 字段也会指向 current Fiber 中对应的 fiber 节点。

fiber 树的构建与更新

下面我们结合源码,来看一下实际工作过程中 fiber 树的构建与更新过程。

mount 过程

react 首次 mount 开始执行时,以 ReactDOM.render 为入口函数,会经过如下一系列的函数调用:ReactDOM.render ——> legacyRenderSubtreeIntoContainer ——> legacyCreateRootFromDOMContainer ——> createLegacyRoot ——> ReactDOMBlockingRoot ——> ReactDOMRoot ——> createRootImpl ——> createContainer ——> createFiberRoot ——> createHostRootFiber ——> createFiber

createFiber 函数中,调用 FiberNode 构造函数,创建了 rootFiber,它是 react 应用的根 fiber:

// packages/react-reconciler/src/ReactFiber.old.js

const createFiber = function(
  tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode,
): Fiber {
  return new FiberNode(tag, pendingProps, key, mode);
};

createFiberRoot 函数中,调用 FiberRootNode 构造函数,创建了 fiberRoot,它指向真实根 dom 节点。

// packages/react-reconciler/src/ReactFiberRoot.old.js

export function createFiberRoot(
  containerInfo: any,  tag: RootTag,  hydrate: boolean,  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
  const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
  if (enableSuspenseCallback) {
    root.hydrationCallbacks = hydrationCallbacks;
  }

  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  initializeUpdateQueue(uninitializedFiber);

  return root;
}

另外 createFiberRoot 函数中,还让 rootFiber 的 stateNode 字段指向了 fiberRoot,fiberRoot 的 current 字段指向了 rootFiber。从而一颗最原始的 fiber 树根节点就创建完成了:

上面的 rootFiber 和 fiberRoot 创建完成后,react 就会根据 jsx 的内容去创建详细的 dom 树了,例如有如下的 jsx:

<div id="root">
  <div id="a1">
    <div id="b1">
      <div id="c1">
        <div id="d1"></div>
        <div id="d2"></div>
        <div id="d3"></div>
      </div>
      <div id="c2"></div>
    </div>
  </div>
</div>

react 对于 fiber 结构的创建和更新,都是采用深度优先遍历,从 rootFiber(此处对应id为root的节点)开始,首先创建 child a1,然后发现 a1 有子节点 b1,继续对 b1 进行遍历,b1 有子节点 c1,再去创建 c1 的子节点 d1、d2、d3,直至发现 d1、d2、d3 都没有子节点来了,再回去创建 c2.

上面的过程,每个节点开始创建时,执行 beginWork 流程,直至该节点的所有子孙节点都创建(更新)完成后,执行 completeWork 流程,过程的图示如下:

update 过程

update 时,react 会根据新的 jsx 内容创建新的 workInProgress fiber,还是通过深度优先遍历,对发生改变的 fiber 打上不同的 flags 副作用标签,并通过 firstEffectnextEffect 等字段形成 Effect List 链表。

例如上面的 jsx 结构,发生了如下的更新:

<div id="root">
  <div id="a1">
    <div id="b1">
      <div id="c1">
        <div id="d1"></div>
-       <div id="d2"></div>
-       <div id="d3"></div>
      </div>
-     <div id="c2"></div>
+     <div id="c2">new content</div>
    </div>
  </div>
</div>

react 会根据新的 jsx 解析后的内容,调用 createWorkInProgress 函数创建 workInProgress fiber,对其标记副作用:

// packages/react-reconciler/src/ReactFiber.old.js

export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  let workInProgress = current.alternate;
  if (workInProgress === null) { // 区分 mount 还是 update
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;

    if (__DEV__) {
      workInProgress._debugID = current._debugID;
      workInProgress._debugSource = current._debugSource;
      workInProgress._debugOwner = current._debugOwner;
      workInProgress._debugHookTypes = current._debugHookTypes;
    }

    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;
    workInProgress.type = current.type;

    workInProgress.subtreeFlags = NoFlags;
    workInProgress.deletions = null;

    if (enableProfilerTimer) {
      workInProgress.actualDuration = 0;
      workInProgress.actualStartTime = -1;
    }
  }

  // 重置所有的副作用
  workInProgress.flags = current.flags & StaticMask;
  workInProgress.childLanes = current.childLanes;
  workInProgress.lanes = current.lanes;

  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;

  // 克隆依赖
  const currentDependencies = current.dependencies;
  workInProgress.dependencies =
    currentDependencies === null
      ? null
      : {
          lanes: currentDependencies.lanes,
          firstContext: currentDependencies.firstContext,
        };

  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;

  if (enableProfilerTimer) {
    workInProgress.selfBaseDuration = current.selfBaseDuration;
    workInProgress.treeBaseDuration = current.treeBaseDuration;
  }

  if (__DEV__) {
    workInProgress._debugNeedsRemount = current._debugNeedsRemount;
    switch (workInProgress.tag) {
      case IndeterminateComponent:
      case FunctionComponent:
      case SimpleMemoComponent:
        workInProgress.type = resolveFunctionForHotReloading(current.type);
        break;
      case ClassComponent:
        workInProgress.type = resolveClassForHotReloading(current.type);
        break;
      case ForwardRef:
        workInProgress.type = resolveForwardRefForHotReloading(current.type);
        break;
      default:
        break;
    }
  }

  return workInProgress;
}

最终生成的 workInProgress fiber 图示如下:

然后如上面所说,current fiber 和 workInProgress fiber 中对应的 alternate 会相互指向,然后 workInProgress fiber 完全创建完成后,fiberRoot 的 current 字段的指向会从 current fiber 中的 rootFiber 改为 workInProgress fiber 中的 rootFiber:

总结

本章讲解了 fiber 出现的主要原因、fiber 节点中主要的属性以及 fiber 树是如何构建与更新的。

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

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

相关文章

AR眼镜新秀雷鸟创新,究竟能飞多远?

时隔近十年之后&#xff0c;消费级AR眼镜又重新高调回归大众视野。 自去年10月开始&#xff0c;以OPPO、小米为代表的国内大厂纷纷推出试验性AR眼镜&#xff0c;谷歌第二代AR眼镜更是作为压轴在I/O大会上重新回归&#xff0c;苹果多年来不断提及但始终“难产”的AR产品&#x…

平衡二叉树(AVL树)

1.简介 1.二叉排序树的问题: 如果原始是数据是排好序的(如1,2,3,4,5,6),那么最终创建的二叉排序树的结构就会变成一条斜线,类似于一条单链表,此时如果需要查找/插入某个元素就要一个一个元素的比较,这样就没有优势了.由于每次都要比较左子树,其查询速度甚至比单链表还慢; 2.对…

labview 写入文本到word报表(标签方法)

描述labview按预先定义的包含数个标签的模板&#xff0c;复制模板到新文件&#xff0c;写入文本到各标签位置。 图1 前面板 图2 程序框图 图3 Ms office report 图4 配置Ms office report的属性1 图5 配置Ms office report的属性2

在vue2项目中使用vue-quill-editor实现富文本编译器

1 安装 npm install vue-quill-editor --save 2 引入 有两种引入方式 &#xff08;1&#xff09;全局引入(main) import VueQuillEditor from vue-quill-editor//调用编辑器 // 样式 import quill/dist/quill.core.css import quill/dist/quill.snow.css import quill/dist…

浅谈无脚本自动化测试

在当今的企业环境中&#xff0c;软件测试不再被视为不必要的投资&#xff1b;相反&#xff0c;它已经上升到一种需要而不是奢侈品的水平。随着市场的不断变化和竞争的加剧&#xff0c;企业必须做一些让他们与竞争对手区分开来的事情。 为了使自己与众不同&#xff0c;公司必须提…

【1-系统架构演进过程】

特别说明:接下来我会和大家一起完成一个商城项目&#xff0c;这个项目涉及的内容以及技术不仅多&#xff0c;而且都是现在主流的开发技术&#xff0c;每天我会按时更新博客内容&#xff0c;详细记录学习的过程&#xff0c;感兴趣的同学可以和我一起完成&#xff0c;但是时间较长…

国际贸易详解:国际贸易主要有哪些分类标准和运输方式

国际贸易主要的分类标准包括按商品流向分为出口贸易&#xff0c;进口贸易和过境贸易&#xff0c;按商品形态分为有形贸易和无形贸易&#xff0c;按运输方式分为陆运贸易&#xff0c;海运贸易等。一、国际贸易主要有哪些分类标准 1、按商品流向分为出口贸易、进口贸易、过境贸易…

2022英特尔® FPGA中国技术周

本文图片均来自于2022英特尔 FPGA中国技术周线上会议 11.14 全新的中端和以边缘为中心的FPGA 英特尔 Agilex™ FPGA的下一代接口协议 11.15 Nios V: 基于FPGA的RISC-V处理器 英特尔 Quartus Prime开发软件 基于FPGA的人工智能开发套件 Case 使用oneAPI高级语言开发IP​ 将…

油罐清洗抽吸系统设计

目录 摘要 - 1 - Abstract - 2 - 1 引言 - 3 - 1.1课题的背景和意义 - 3 - 1.1.1课题的背景 - 3 - 1.1.2课题的意义 - 3 - 1.2国内外油罐清洗抽吸系统的研究情况 - 3 - 2 油罐清洗抽吸系统总体设计方案 - 6 - 2.1油罐清洗抽吸系统方案 - 6 - 2.2清洗对象及要求 - 6 - 2.3清洗工…

欧拉路径!

呃在昨晚的破防之下我并不想学这东西所以留到今晚&#xff1f;属于是懒爆了。那么我们来看&#xff0c;定义的话其实前面写过了 其实主要是分两个方面: 1.图是否联通&#xff0c;是什么图 2.这个图每个点的度数或者&#xff08;出度入度&#xff09;什么的若是符合就行。 偷偷总…

【C++】C++基础知识(四)---程序流程结构

C基础知识&#xff08;四&#xff09;1. 顺序结构2. 选择结构2.1 if语句2.2 switch语句2.3 选择结构案例3. 循环结构3.1 循环语句3.2 循环结构案例4. 跳转语句C中支持的三种流程结构&#xff1a;顺序结构、选择结构、循环结构顺序结构&#xff1a;程序按照顺序执行&#xff0c;…

easy-monitor3.0 nodejs性能监控和分析工具

#easy-monitor性能监控和分析工具 Easy-Monitor 3.0 https://blog.csdn.net/qq_36791889/article/details/115420116 #git地址&#xff1a;https://github.com/1981430140/easy-monitor-docker-compose.git 一、easy-monitor 服务器端安装&#xff08;docker-compose&#xff…

我说MySQL里每张表不要超过100w数据,面试官让我回去等通知?

V-xin&#xff1a;ruyuanhadeng获得600页原创精品文章汇总PDF 目录 1、面试题2、面试官心理分析3、面试题剖析 1、面试题 事务的几个特点是什么&#xff1f;数据库事务有哪些隔离级别&#xff1f;MySQL的默认隔离级别&#xff1f; 2、面试官心里分析 用mysql开发的三个基本…

操作系统4小时速成:处理机调度,调度方法,调度准则,典型的调度算法,响应比

操作系统4小时速成&#xff1a;处理机调度&#xff0c;调度方法&#xff0c;调度准则&#xff0c;典型的调度算法&#xff0c;响应比 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff…

详探XSS PayIoad

详探XSS PayIoad1.Cookie劫持2.构造GET与POST请求3.XSS钓鱼4.识别用户浏览器1.Cookie劫持 一个最常见的XSS Payload&#xff0c;就是通过读取浏览器的Cookie对象&#xff0c;从而发起“Cookie劫持”攻击 Cookie中一般加密保存了当前用户的登录凭证。Cookie如果丢失&#xff0…

码神之路项目部署(五)

这一章主要是讲解一下怎么部署上线项目 一、上线准备 腾讯云服务器一台&#xff08;2核2g&#xff09;、域名一个 &#xff08;可有可无&#xff09; 说明&#xff1a;当然了服务器的配置根据项目来选择&#xff0c;2核2g跑这个博客小项目是完全没有问题的 二、服务器安装环…

但见新人笑,那闻旧人哭,大衣哥前儿媳陈亚楠好可怜

著名爱国艺人黄安&#xff0c;曾经演唱过一首歌曲&#xff0c;名字叫作《新鸳鸯蝴蝶梦》&#xff0c;歌词写的是相当棒。比如说“由来只有新人笑&#xff0c;有谁听到旧人哭”这句歌词&#xff0c;就是农民歌唱家大衣哥&#xff0c;前儿媳陈亚楠的真实写照。 陈亚楠之所以和大衣…

迈动互联获“ISO20000信息技术服务管理体系认证证书”

近日&#xff0c;迈动互联获得“ISO20000信息技术服务管理体系标准”认证证书&#xff0c;该证书标志着迈动在IT服务管理标准领域的关键技术取得了制度流程规范性和完备性上的进一步提升。 此前&#xff0c;迈动已经获得专精特新企业评定、ISO9001质量管理体系认证、27001信息…

[附源码]java毕业设计基于协同过滤推荐的电影推荐系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Invertible Image Signal Processing 可逆图像信号处理

摘要 未经处理的 RAW 数据是一种非常有价值的图像格式&#xff0c;可用于图像编辑和计算机视觉。然而&#xff0c;由于 RAW 数据的文件大小巨大&#xff0c;大多数用户只能访问经过处理和压缩的 sRGB 图像。为了弥合这一差距&#xff0c;我们设计了一个可逆图像信号处理 (InvIS…