react源码中的fiber架构
先看一下FiberNode在源码中的样子
FiberNode
// packages/react-reconciler/src/ReactFiber.old.js
function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode,
) {// Instancethis.tag = tag;this.key = key;this.elementType = null;this.type = null;this.stateNode = null;// Fiberthis.return = null;this.child = null;this.sibling = null;this.index = 0;this.ref = null;this.pendingProps = pendingProps;this.memoizedProps = null;this.updateQueue = null;this.memoizedState = null;this.dependencies = null;this.mode = mode;// Effectsthis.flags = NoFlags;this.nextEffect = null;this.firstEffect = null;this.lastEffect = null;this.lanes = NoLanes;this.childLanes = NoLanes;this.alternate = null;if (enableProfilerTimer) {// Note: The following is done to avoid a v8 performance cliff.//// Initializing the fields below to smis and later updating them with// double values will cause Fibers to end up having separate shapes.// This behavior/bug has something to do with Object.preventExtension().// Fortunately this only impacts DEV builds.// Unfortunately it makes React unusably slow for some applications.// To work around this, initialize the fields below with doubles.//// Learn more about this here:// https://github.com/facebook/react/issues/14365// https://bugs.chromium.org/p/v8/issues/detail?id=8538this.actualDuration = Number.NaN;this.actualStartTime = Number.NaN;this.selfBaseDuration = Number.NaN;this.treeBaseDuration = Number.NaN;// It's okay to replace the initial doubles with smis after initialization.// This won't trigger the performance cliff mentioned above,// and it simplifies other profiler code (including DevTools).this.actualDuration = 0;this.actualStartTime = -1;this.selfBaseDuration = 0;this.treeBaseDuration = 0;}if (__DEV__) {// This isn't directly used but is handy for debugging internals:...}
}
- 我们看
FiberNode这个构造函数里面只是赋值,我们再找一下链路上的Fiber,我们发现在函数createFiber的返回值类型里面出现了Fiber类型,所以
// packages/react-reconciler/src/ReactInternalTypes.js
export type Fiber = {|// These first fields are conceptually members of an Instance. This used to// be split into a separate type and intersected with the other Fiber fields,// but until Flow fixes its intersection bugs, we've merged them into a// single type.// An Instance is shared between all versions of a component. We can easily// break this out into a separate object to avoid copying so much to the// alternate versions of the tree. We put this on a single object for now to// minimize the number of objects created during the initial render.// dom节点的相关信息tag: WorkTag,// 组件的类型key: null | string, // 唯一值elementType: any,// 元素类型// 判定fiber节点的类型,用于difftype: any,// 真实 dom 节点stateNode: any,// Conceptual aliases// parent : Instance -> return The parent happens to be the same as the// return fiber since we've merged the fiber and instance.// Remaining fields belong to Fiber// fiber 链表树return: Fiber | null, // 父 fiberchild: Fiber | null, // 第一个子 fibersibling: Fiber | null, // 下一个兄弟 fiberindex: number, // 在父 fiber 下面的子 fiber 中的下标// The ref last used to attach this node.// I'll avoid adding an owner field for prod and model that as functions.ref:| null| (((handle: mixed) => void) & {_stringRef: ?string, ...})| RefObject,// 计算 state 和 props 渲染pendingProps: any, // 本次渲染需要使用的 propsmemoizedProps: any, // 上次渲染使用的 propsupdateQueue: mixed, // 用于状态更新、回调函数、DOM更新的队列memoizedState: any, // 上次渲染后的 state 状态dependencies: Dependencies | null, // contexts、events 等依赖// Bitfield that describes properties about the fiber and its subtree. E.g.// the ConcurrentMode flag indicates whether the subtree should be async-by-// default. When a fiber is created, it inherits the mode of its// parent. Additional flags can be set at creation time, but after that the// value should remain unchanged throughout the fiber's lifetime, particularly// before its child fibers are created.mode: TypeOfMode,// Effectflags: Flags, // 记录更新时当前 fiber 的副作用(删除、更新、替换等)状态subtreeFlags: Flags, // 当前子树的副作用状态deletions: Array<Fiber> | null, // 要删除的子 fibernextEffect: Fiber | null, // 下一个有副作用的 fiberfirstEffect: Fiber | null, // 指向第一个有副作用的 fiberlastEffect: Fiber | null, // 指向最后一个有副作用的 fiber // 渲染优先级lanes: Lanes,childLanes: Lanes,// This is a pooled version of a Fiber. Every fiber that gets updated will// eventually have a pair. There are cases when we can clean up pairs to save// memory if we need to.alternate: Fiber | null,// 指向 workInProgress fiber 树中对应的节点// Time spent rendering this Fiber and its descendants for the current update.// This tells us how well the tree makes use of sCU for memoization.// It is reset to 0 each time we render and only updated when we don't bailout.// This field is only set when the enableProfilerTimer flag is enabled.actualDuration?: number,// If the Fiber is currently active in the "render" phase,// This marks the time at which the work began.// This field is only set when the enableProfilerTimer flag is enabled.actualStartTime?: number,// Duration of the most recent render time for this Fiber.// This value is not updated when we bailout for memoization purposes.// This field is only set when the enableProfilerTimer flag is enabled.selfBaseDuration?: number,// Sum of base times for all descendants of this Fiber.// This value bubbles up during the "complete" phase.// This field is only set when the enableProfilerTimer flag is enabled.treeBaseDuration?: number,// Conceptual aliases// workInProgress : Fiber -> alternate The alternate used for reuse happens// to be the same as work in progress.// __DEV__ only_debugID?: number,_debugSource?: Source | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,_debugNeedsRemount?: boolean,// Used to verify that the order of hooks does not change between renders._debugHookTypes?: Array<HookType> | null,
|};
相关参考视频讲解:进入学习
- 整个fiber架构看起来可以分为dom信息、副作用、优先级、链表树等几个模块,那我们依次来拆分一下
dom信息节点
tag: WorkTag
我们看到这个`tag`为`WorkTag`类型,用来区分`React`组件的类型
// packages/react-reconciler/src/ReactWorkTags.js
export type WorkTag =| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24;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;
上述代码,区分了组件的类型,在后期协调阶段beginWork、completeWork的流程里根据不同的类型组件去做不同的fiber节点的处理
key: null | string、type: any
key为唯一值,type为与fiber关联的节点类型,都用于beginWork流程里面的reconcileChildren流程
elementType
元素类型
stateNode
- stateNode 用于记录当前 fiber 所对应的真实 dom 节点或者当前虚拟组件的实例。
- 便于实现Ref
- 便于追踪Rdom
fiber 链表树
fiber链表树里面有四个字段return、child、sibling、index
- return:指向父节点,没有父节点则为null。
- child:指向下一个子节点,没有下一个子节点则为null。
- sibling:指向兄弟节点,没有下一个兄弟节点则为null。
- index:父fiber下面的子fiber下标
通过这些字段那么我们可以形成一个闭环链表,举个栗子。
<div className='box'><h1 className='title' style={{'color':'red'}}>React源码解析</h1><ul><li>第一章</li><li>第二章</li><li>第三章</li><li>第四章</li></ul>
</div>
根据上面的代码所对应的fiber链表树结构就是:

副作用相关
所谓副作用就是一套流程中我们不期望发生的情况。举个通俗的例子就是我们生活中去学游泳,在学会游泳的过程中呛了几口水,这个呛了几口水相对于成功学会游泳来说就是副作用,回归到react代码中,我们通过某些手段去修改props、state等数据,数据修改完毕之后,但是同时引起了dom不必要的变化,那么这个变化就是副作用,当然这个副作用是必然存在的,就像游泳一样,必然会呛几口水,哈哈。
flags: Flags
记录当前节点通过`reconcileChildren`之后的的副作用,如插入,删除等
例如Placement,表示插入,也叫新增。Deletion表示删除,Update表示更新
export type Flags = number;// Don't change these two values. They're used by React Dev Tools.
export const NoFlags = /* */ 0b000000000000000000;
export const PerformedWork = /* */ 0b000000000000000001;// You can change the rest (and add more).
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;
// TODO (effects) Remove this bit once the new reconciler is synced to the old.
export const PassiveUnmountPendingDev = /* */ 0b000010000000000000;
export const Hydrating = /* */ 0b000000010000000000;
export const HydratingAndUpdate = /* */ 0b000000010000000100;// Passive & Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /* */ 0b000000001110100100;// Union of all host effects
export const HostEffectMask = /* */ 0b000000011111111111;// These are not really side effects, but we still reuse this field.
export const Incomplete = /* */ 0b000000100000000000;
export const ShouldCapture = /* */ 0b000001000000000000;
export const ForceUpdateForLegacySuspense = /* */ 0b000100000000000000;// Static tags describe aspects of a fiber that are not specific to a render,
// e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
// This enables us to defer more work in the unmount case,
// since we can defer traversing the tree during layout to look for Passive effects,
// and instead rely on the static flag as a signal that there may be cleanup work.
export const PassiveStatic = /* */ 0b001000000000000000;// Union of side effect groupings as pertains to subtreeFlags
export const BeforeMutationMask = /* */ 0b000000001100001010;
export const MutationMask = /* */ 0b000000010010011110;
export const LayoutMask = /* */ 0b000000000010100100;
export const PassiveMask = /* */ 0b000000001000001000;// Union of tags that don't get reset on clones.
// This allows certain concepts to persist without recalculting them,
// e.g. whether a subtree contains passive effects or portals.
export const StaticMask = /* */ 0b001000000000000000;// These flags allow us to traverse to fibers that have effects on mount
// without traversing the entire tree after every commit for
// double invoking
export const MountLayoutDev = /* */ 0b010000000000000000;
export const MountPassiveDev = /* */ 0b100000000000000000;
当然副作用不仅仅只是一个,所以React中在render阶段中采用的是深度遍历的策略去找出当前fiber树中所有的副作用,并维护一个副作用链表EffectList,与链表相关的字段还有firstEffect、nextEffect 和 lastEffect 我们来画一张图来简略示意一下。

解读一下就是,fristEffect指向第一个有副作用的fiber节点,lastEffect指向最后一个具有副作用的fiber节点,中间都是用nextEffect链接,这样组成了一个单向链表。
render阶段里面这一段处理就完了,在后面的commit阶段里面,React会根据EffectList里面fiber节点的副作用,会对应的处理相应的DOM,然后生成无副作用的虚拟节点,进行真实dom的创建。
优先级相关
当然React作为一个庞大的框架,肯定有自己的一套关于渲染的优先级机制,不然全都是一股脑按部就班的走,那肯定不行哒。
那么优先级我们就要关注一下lane与alternate,React中每个fiber任务都有自己的lane(执行优先级),这样在render阶段react才知道,应该优先把哪个fiber任务提交到commit阶段去执行。而alternate是在render阶段中用来做为指针的,什么意思?React在状态发生改变的时候,就会根据当前的页面结构,生成两棵fiber树,一棵老的称之为current Fiber,而另一棵将要生成新的页面的树叫做workInProgress Fiber,而alternate作为指针,就是把current Fiber中的每一个节点指向workInProgress Fiber中的每一个节点。同样的他也会从workInProgress Fiber中指向 current Fiber

我们了解到了alternate,那就来说一说这个lane吧。
// packages/react-reconciler/src/ReactFiberLane.js
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /* */ 0b0000000000000000000000000000000;export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010;export const 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;
可见这个lane也是用31位二进制表示的唯一值,来进行优先级的判定的,并且位数越低,则优先级越大。
Props && State相关
pendingProps: any
本次渲染需要使用的 props
memoizedProps: any
上次渲染使用的 props
updateQueue: mixed
用于状态更新、回调函数、DOM更新的队列
memoizedState: any
上次渲染后的 state 状态
dependencies: Dependencies | null
contexts、events 等依赖
Fiber树的创建与更新的流程
上面一部分讲了React Fiber的基本架构,从真实dom信息、副作用、优先级等方面看了一下,为后面的render阶段的协调与调度以及commit阶段打下基础,那么接下来我们去探讨一下new FiberNode之后得到的什么样的rootFiber。
我们在第二章节里面提到了整个的创建过程# React源码解析系列(二) – 初始化组件的创建更新流程,那么这里深入探讨一下createFiber,在这个函数里面new FiberNode,创建了rootFiber,他也就是整个React应用的的根fiber。并且在createFiberRoot里面new FiberRootNode,创建了fiberRoot,它便是指向真实dom的根节点。所以在# React源码解析系列(二) – 初始化组件的创建更新流程中我强调了root.current、uninitializedFiber.stateNode这两个东西,也就是这里说的rootFiber的stateNode字段指向了 fiberRoot,并且fiberRoot的current指向了rootFiber,具体的示例图如下:

所以这里就完成了fiber树根节点的创建了。
拿到了上面创建完成的rootFiber和fiberRoot之后那么我们接下来就是去根据我们的组件jsx去创建详细的dom树了,举个例子:
<div className='box'><h1 className='title' style={{'color':'red'}}>React源码解析</h1><ul><li>第一章</li><li>第二章</li><li>第三章</li><li>第四章</li></ul>
</div>
现有上面的jsx,那么我们创建dom树的形式是深度优先遍历,已beginwork和completework表示一个节点的创建过程,流程如下:

上面的图说明了,在初始化的时候我们的dom树是怎么被创建出来的,那么在状态发生改变的时候,我们会根据当前新的jsx内容创建新的workInProgress fiber,我们以新的jsx为例:
<div className='box'>
<h1 className='title' style={{'color':'red'}}>React源码解析</h1>
+ <h1 className='title' style={{'color':'red'}}>React源码解析系列</h1><ul><li>第一章</li><li>第二章</li><li>第三章</li>
- <li>第四章</li></ul>
+ <p>总结</p>
</div>
上面的jsx表示,更改了h1的内容,删除了第四章,增加了总结这几个操作,那么react根据当前新的jsx调用createWorkInProgress方法创建workInProgress fiber,那么我们先去看一下createWorkInProgress的源码实现。
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {let workInProgress = current.alternate;if (workInProgress === null) { // null为初始化,否为update// We use a double buffering pooling technique because we know that we'll// only ever need at most two versions of a tree. We pool the "other" unused// node that we're free to reuse. This is lazily created to avoid allocating// extra objects for things that are never updated. It also allow us to// reclaim the extra memory if needed.workInProgress = createFiber(current.tag,pendingProps,current.key,current.mode,);workInProgress.elementType = current.elementType;workInProgress.type = current.type;workInProgress.stateNode = current.stateNode;if (__DEV__) {// DEV-only fieldsworkInProgress._debugID = current._debugID;workInProgress._debugSource = current._debugSource;workInProgress._debugOwner = current._debugOwner;workInProgress._debugHookTypes = current._debugHookTypes;}// current 指向 workInProgressworkInProgress.alternate = current;// workInProgress 指向 currentcurrent.alternate = workInProgress;} else {// 上一次的propsworkInProgress.pendingProps = pendingProps;// Needed because Blocks store data on type.workInProgress.type = current.type;// We already have an alternate.// Reset the effect tag.//清除flagsworkInProgress.flags = NoFlags;// The effect list is no longer valid.workInProgress.nextEffect = null;workInProgress.firstEffect = null;workInProgress.lastEffect = null;if (enableProfilerTimer) {// We intentionally reset, rather than copy, actualDuration & actualStartTime.// This prevents time from endlessly accumulating in new commits.// This has the downside of resetting values for different priority renders,// But works for yielding (the common case) and should support resuming.workInProgress.actualDuration = 0;workInProgress.actualStartTime = -1;}}// 绑定挂载的子fiber节点优先级、状态、propsworkInProgress.childLanes = current.childLanes;workInProgress.lanes = current.lanes;workInProgress.child = current.child;workInProgress.memoizedProps = current.memoizedProps;workInProgress.memoizedState = current.memoizedState;workInProgress.updateQueue = current.updateQueue;// Clone the dependencies object. This is mutated during the render phase, so// it cannot be shared with the current fiber.const currentDependencies = current.dependencies;workInProgress.dependencies =currentDependencies === null? null: {lanes: currentDependencies.lanes,firstContext: currentDependencies.firstContext,};// These will be overridden during the parent's reconciliationworkInProgress.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;
}
并且为其标记副作用,具体如下:

而前面所说的alternate在这里相互指向,其实也就是在reconciler阶段起到了复用节点的作用,因为我们所说的current fiber或者是workInProgress fiber都是视图的产物,是可以在"新"与"老"之间转换的。
为什么会出现Fiber架构呢?
相信在座的各位写React的同学出去面试,面试官总会问:”请问你知道React Fiber架构吗?请你说说Fiber架构吧“
为什么会出现?通过上面的React Fiber架构的讲解,我们可以get到几个点,那就是fiber针对每一个fiber节点都会有一套自己的独立的beginwork和completework,并且能够在每一个具有副作用的节点上进行打标处理,而不是直接变更。而且生成的current fiber与workIProgress fiber可以相互转换,这里间接地可以称之为缓存吧。对比与以前的React应用来讲,以前的React应用是根据执行生命周期、diff、dom的更新一套流程同步走的,一套流程下来,不能中断,而且每一次的更新都是从根节点出发向下遍历的,我们可以设想一下处理庞大的结构的时候,那将是不可想象的性能开销,处理长时间任务耗时更长,更重要的是用户的交互,事件得不到及时响应,用户体验非常的差。
但是fiber这种结构,我们说的是一种时间分片的概念,通过时间分片把长任务,分成一个个独立的小单元去执行,返回。这样子就不会让js线程被React应用独占,能有有空余去处理其他优先级较高的任务,任务得到了相应并且执行,当然了这种情况下页面就不会显得卡顿了。
所以总结来说就是React Fiber给我们提供了一种协调,调度,暂停,中止,调优的方式去更好的处理React应用与浏览器的工作,保证了页面的性能与流畅度

总结
这一章讲述了整个的fiber架构与fiber树的创建与更新,那么这里从React应用的初始化挂载到React更新就形成了一部分的闭环完结,之后我们便是沿着流程走到了updateContainer更新这里
相关文章:
react源码中的fiber架构
先看一下FiberNode在源码中的样子 FiberNode // packages/react-reconciler/src/ReactFiber.old.js function FiberNode(tag: WorkTag, pendingProps: mixed, key: null | string, mode: TypeOfMode, ) {// Instancethis.tag tag;this.key key;this.elementType null;t…...
C++类和对象-继承多态
继承 继承是面向对象三大特性之一 定义类时,下级别的成员除了拥有上一级的共性,还有自己的特性,就可以考虑使用继承的技术,减少代码的重复 继承的基本语法 语法:class 子类 : 继承方式 父类 子类也被成为派生类父类…...
appium自动化测试
获取应用包名和入口activity:aapt命令 aapt目录: 安卓sdk的build-tools目录下(如果要在cmd里直接运行,要配置环境变量,否则需要在aapt所在目录下打开cmd) 示例: adt-bundle-windows-x86_64-20140702\sdk\build-too…...
打印流、转换流、数据流 、随机访问流
Java知识点总结:想看的可以从这里进入 目录5、打印流6、转换流7、数据流8、随机访问流5、打印流 实现将基本数据类型的数据格式转化为字符串输出,它们提供了一系列重载的print()和println()方法,用于多种数据类型的输出,这种流不会…...
Java的4种访问权限?
1、public: 所修饰的类、变量、方法,在内外包均具有访问权限;2、protected: 这种权限是为继承而设计的,protected所修饰的成员,对所有子类是可访问的,但只对同包的类是可访问的,对外…...
APP任务模块功能借助php-resque实现业务解耦
先上设计图 说明:任务模块分一次性任务和每日任务,可能还包括男女用户任务区分 处理步骤: 一、同步任务数据库 1.1、任务列表数据库 1.2、完成任务数据库 二、搭建即时消息队列 一、composer require resque/php-resque二、因为服务器red…...
怎么做,才能在职场中晋升?
1 主动原则:主动做事 工作要积极主动,刚进入职场的同学,以为“服从命令听指挥”“领导指哪打哪”就是积极主动,结果易养 1.1 不好习惯 ① 认为主管肯定会帮你搞定晋升 你可能非常信任主管,认为自己只要把主管安排的…...
Vulnhub靶场----2、DC-2
文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-2下载地址:https://download.vulnhub.com/dc/DC-2.zip kali:192.168.144.148 DC-2:192.168.144.150 添加hosts文件:192.168.144.150 DC-2 二、渗透流程 nmap -A -…...
Java 基础(3)—synchornized 关键字简单理解
一、synchronized 修饰同步代码块 用 synchronized 修饰代码段作为同步锁,代码如下: public class LockDemo {public Object object new Object();public void show(){synchronized (object) {System.out.println(">>>>>>hell…...
【Linux】调试工具gdb的使用
环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹前言在前文,我们已经讲解了vim工具以及gcc/g的使用,我们可以进行编写代码以及编译代码了,但是还没有学习如何在Linu…...
大数据知识图谱项目——基于知识图谱的医疗知识问答系统(详细讲解及源码)
基于知识图谱的医疗知识问答系统 一、项目概述 本项目基于医疗方面知识的问答,通过搭建一个医疗领域知识图谱,并以该知识图谱完成自动问答与分析服务。本项目以neo4j作为存储,基于传统规则的方式完成了知识问答,并最终以关键词执…...
威马汽车:跃马扬鞭未竟,鞍马劳顿难行?
“活下去,像牲口一样地活下去。” 威马汽车创始人、董事长兼CEO沈晖1月在社交媒体上分享的电影台词,已然成为威马近况的真实写照。 来源:新浪微博威马汽车沈晖Freeman 最近,网上出现了大量关于“威马汽车将实施全员停薪留职”的…...
【网络】网络基础
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
Linux系统之Uboot、Kernel、Busybox思考之三
目录 三 内核的运行 5-中断子系统 6 锁、延迟与原子上下文 7 内存管理子系统 8 驱动的两类框架 三 内核的运行 5-中断子系统 中断子系统的数据结构及设计思想。 中断子系统需要解决中断管理的问题。 如果系统中断较少的话,其管理就不用设计这样一个中断子系统这…...
FPGA 20个例程篇:20.USB2.0/RS232/LAN控制并行DAC输出任意频率正弦波、梯形波、三角波、方波(一)
在最后一个例程中笔者精挑细选了一个较为综合性的项目实战,其中覆盖了很多知识点,也是从一个转产产品中所提炼出来的,所以非常贴近实战项目。 整个工程实现了用户通过对上位机PC端人机界面的操作,即可达到控制豌豆开发并行DAC输出…...
性能测试学习和性能瓶颈分析路线
很多企业招聘都只写性能测试,会使用LR,jmeter工具。其实会使用jmeter和LR进行性能测试还只是性能测试的第一步,离真正的性能测试工程师还很远,笔者也还在路上 .。 性能测试,都是要求测试系统性能,系统自然…...
达梦数据库(DM8)集成使用 Geoserver(2.22.2) 以及其他对应版本详解
达梦数据库(DM8)集成使用 Geoserver(2.22.2) 以及其他对应版本详解系统环境版本Geoserver 驱动对应版本达梦 8 集成 Geoserver 过程试错过程问题总结项目需要国产化,选择使用达梦数据库,在技术测试阶段&…...
全开源无加密的RuleApp文章社区APP客户端源码
内容目录一、详细介绍二、效果展示1.部分代码2.效果图展示三、学习资料下载一、详细介绍 开源无加密的文章社区客户端源码分享 RuleApp文章社区,VIP会员,写作投稿积分商城,付费模块集成,多平台兼容这是一款开源免费,界…...
基于springboot校园二手市场平台
一、项目简介 本项目是一套基于springboot校园二手市场平台,主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含:项目源码、数据库脚本等,该项目可以直接作为bishe使用。 项目都经过严格调试,确保…...
维度建模基本流程总结
一、维度建模基本流程图数据RD进行业务调研和数据现状调研,产出符合相关模版规范的业务知识文档和数据现状文档。数据PM也会调研相关业务产出需求设计文档,三方参与需求评审,评审通过后基建数据RD进行需求拆解,产出技术方案&#…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
32位寻址与64位寻址
32位寻址与64位寻址 32位寻址是什么? 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元(地址),其核心含义与能力如下: 1. 核心定义 地址位宽:CPU或内存控制器用32位…...
