当前位置: 首页 > news >正文

从源码角度看React-Hydrate原理

React 渲染过程,即ReactDOM.render执行过程分为两个大的阶段:render 阶段以及 commit 阶段。React.hydrate渲染过程和ReactDOM.render差不多,两者之间最大的区别就是,ReactDOM.hydraterender 阶段,会尝试复用(hydrate)浏览器现有的 dom 节点,并相互关联 dom 实例和 fiber,以及找出 dom 属性和 fiber 属性之间的差异。

Demo

这里,我们在 index.html 中直接返回一段 html,以模拟服务端渲染生成的 html

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><title>Mini React</title><meta name="viewport" content="width=device-width, initial-scale=1" /></head><body><div id="root"><div id="root"><div id="container"><h1 id="A">1<div id="A2">A2</div></h1><p id="B"><span id="B1">B1</span></p><span id="C">C</span></div></div></div></body>
</html>

注意,root 里面的内容不能换行,不然客户端hydrate的时候会提示服务端和客户端的模版不一致。

新建 index.jsx:

import React from "react";
import ReactDOM from "react-dom";
class Home extends React.Component {constructor(props) {super(props);this.state = {count: 1,};}render() {const { count } = this.state;return (<div id="container"><div id="A">{count}          <div id="A2">A2</div></div><p id="B"><span id="B1">B1</span></p></div>);}
}ReactDOM.hydrate(<Home />, document.getElementById("root"));

对比服务端和客户端的内容可知,服务端h1#A和客户端的div#A不同,同时服务端比客户端多了一个span#C

在客户端开始执行之前,即 ReactDOM.hydrate 开始执行前,由于服务端已经返回了 html 内容,浏览器会立马显示内容。对应的真实 DOM 树如下:

在这里插入图片描述

注意,这不是 fiber 树!!

ReactDOM.render

先来回顾一下 React 渲染更新过程,分为两大阶段,五小阶段:

  • render 阶段
    • beginWork
    • completeUnitOfWork
  • commit 阶段。
    • commitBeforeMutationEffects
    • commitMutationEffects
    • commitLayoutEffects

React 在 render 阶段会根据新的 element tree 构建 workInProgress 树,收集具有副作用的 fiber 节点,构建副作用链表。

特别是,当我们调用ReactDOM.render函数在客户端进行第一次渲染时,render阶段的completeUnitOfWork函数针对HostComponent以及HostText类型的 fiber 执行以下 dom 相关的操作:

    1. 调用document.createElementHostComponent类型的 fiber 节点创建真实的 DOM 实例。或者调用document.createTextNodeHostText类型的 fiber 节点创建真实的 DOM 实例
    1. 将 fiber 节点关联到真实 dom 的__reactFiber$rsdw3t27flk(后面是随机数)属性上。
    1. 将 fiber 节点的pendingProps 属性关联到真实 dom 的__reactProps$rsdw3t27flk(后面是随机数)属性上
    1. 将真实的 dom 实例关联到fiber.stateNode属性上:fiber.stateNode = dom
    1. 遍历 pendingProps,给真实的dom设置属性,比如设置 id、textContent 等

React 渲染更新完成后,React 会为每个真实的 dom 实例挂载两个私有的属性:__reactFiber$__reactProps$,以div#container为例:

在这里插入图片描述

ReactDOM.hydrate

hydrate中文意思是水合物,这样理解有点抽象。根据源码,我更乐意将hydrate的过程描述为:React 在 render 阶段,构造 workInProgress 树时,同时按相同的顺序遍历真实的 DOM 树,判断当前的 workInProgress fiber 节点和同一位置的 dom 实例是否满足hydrate的条件,如果满足,则直接复用当前位置的 DOM 实例,并相互关联 workInProgress fiber 节点和真实的 dom 实例,比如:

fiber.stateNode = dom;
dom.__reactProps$ = fiber.pendingProps;
dom.__reactFiber$ = fiber;

如果 fiber 和 dom 满足hydrate的条件,则还需要找出dom.attributesfiber.pendingProps之间的属性差异。

遍历真实 DOM 树的顺序和构建 workInProgress 树的顺序是一致的。都是深度优先遍历,先遍历当前节点的子节点,子节点都遍历完了以后,再遍历当前节点的兄弟节点。因为只有按相同的顺序,fiber 树同一位置的 fiber 节点和 dom 树同一位置的 dom 节点才能保持一致

只有类型为HostComponent或者HostText类型的 fiber 节点才能hydrate。这一点也很好理解,React 在 commit 阶段,也就只有这两个类型的 fiber 节点才需要执行 dom 操作。

fiber 节点和 dom 实例是否满足hydrate的条件:

  • 对于类型为HostComponent的 fiber 节点,如果当前位置对应的 DOM 实例nodeTypeELEMENT_NODE,并且fiber.type === dom.nodeName,那么当前的 fiber 可以混合(hydrate)

  • 对于类型为HostText的 fiber 节点,如果当前位置对应的 DOM 实例nodeTypeTEXT_NODE,同时fiber.pendingProps不为空,那么当前的 fiber 可以混合(hydrate)

hydrate的终极目标就是,在构造 workInProgress 树的过程中,尽可能的复用当前浏览器已经存在的 DOM 实例以及 DOM 上的属性,这样就无需再为 fiber 节点创建 DOM 实例,同时对比现有的 DOM 的attribute以及 fiber 的pendingProps,找出差异的属性。然后将 dom 实例和 fiber 节点相互关联(通过 dom 实例的__reactFiber$以及__reactProps$,fiber 的 stateNode 相互关联)

hydrate 过程

React 在 render 阶段构造HostComponent或者HostText类型的 fiber 节点时,会首先调用 tryToClaimNextHydratableInstance(workInProgress) 方法尝试给当前 fiber 混合(hydrate)DOM 实例。如果当前 fiber 不能被混合,那当前节点的所有子节点在后续的 render 过程中都不再进行hydrate,而是直接创建 dom 实例。等到当前节点所有子节点都调用completeUnitOfWork完成工作后,又会从当前节点的兄弟节点开始尝试混合。

以下面的 demo 为例,

// 服务端返回的DOM结构,这里为了直观,我格式化了一下,按理服务端返回的内容,是不允许换行或者有空字符串的
<body><div id="root"><div id="container"><h1 id="A">1        <div id="A2">A2</div></h1><p id="B"><span id="B1">B1</span></p><span id="C">C</span></div></div>
</body>
// 客户端生成的内容
<div id="container"><div id="A">1    <div id="A2">A2</div></div><p id="B"><span id="B1">B1</span></p>
</div>

render 阶段,按以下顺序:

    1. div#container 满足hydrate的条件,因此关联 dom,fiber.stateNode = div#container。然后使用hydrationParentFiber记录当前混合的 fiber 节点:hydrationParentFiber = fiber。获取下一个 DOM 实例,这里是h1#A,保存在变量nextHydratableInstance中,nextHydratableInstance = h1#A

这里,hydrationParentFibernextHydratableInstance 都是全局变量。

    1. div#Ah1#A 不能混合,这时并不会立即结束混合的过程,React 继续对比h1#A的兄弟节点,即p#B,发现div#A还是不能和p#B混合,经过最多两次对比,React 认为 dom 树中已经没有 dom 实例满足和div#A这个 fiber 混合的条件,于是div#A节点及其所有子孙节点都不再进行混合的过程,此时将isHydrating设置为 false 表明div#A这棵子树都不再走混合的过程,直接走创建 dom 实例。同时控制台提示:Expected server HTML to contain a matching.. 之类的错误。
    1. beginWork 执行到文本节点 1 时,发现 isHydrating = false,因此直接跳过混合的过程,在completeUnitOfWork阶段直接调用document.createTextNode直接为其创建文本节点
    1. 同样的,beginWork 执行到节点div#A2时,发现isHydrating = false,因此直接跳过混合的过程,在completeUnitOfWork阶段直接调用document.createElement直接为其创建真实 dom 实例,并设置属性
    1. 由于div#A的子节点都已经completeUnitWork了,轮到div#A调用completeUnitWork完成工作,将hydrationParentFiber指向其父节点,即div#container这个 dom 实例。设置isHydrating = true表明可以为当前节点的兄弟节点继续混合的过程了。div#A没有混合的 dom 实例,因此调用document.createElement为其创建真实的 dom 实例。
    1. p#B执行 beginWork。由于nextHydratableInstance保存的还是h1#Adom 实例,因此p#Bh1#A对比发现不能复用,React 尝试和h1#A的兄弟节点p#B对比,发现 fiberp#B和 domp#B能混,因此将h1#A标记为删除,同时关联 dom 实例:fiber.stateNode = p#B,保存hydrationParentFiber = fibernextHydratableInstance指向p#B的第一个子节点,即span#B1

…省略了后续的过程。

从上面的执行过程可以看出,hydrate 的过程如下:

  • 调用 tryToClaimNextHydratableInstance 开始混合
  • 判断当前 fiber 节点和同一位置的 dom 实例是否满足混合的条件。
  • 如果当前位置的 dom 实例不满足混合条件,则继续比较当前 dom 的兄弟元素,如果兄弟元素和当前的 fiber 也不能混合,则当前 fiber 及其所有子孙节点都不能混合,后续 render 过程将会跳过混合。直到当前 fiber 节点的兄弟节点 render,才会继续混合的过程。

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

事件绑定

React在初次渲染时,不论是ReactDOM.render还是ReactDOM.hydrate,会调用createRootImpl函数创建fiber的容器,在这个函数中调用listenToAllSupportedEvents注册所有原生的事件。

function createRootImpl(container, tag, options) {// ...var root = createContainer(container, tag, hydrate);// ...listenToAllSupportedEvents(container);// ...return root;
}

这里container就是div#root节点。listenToAllSupportedEvents会给div#root节点注册浏览器支持的所有原生事件,比如onclick等。React合成事件一文介绍过,React采用的是事件委托的机制,将所有事件代理到div#root节点上。以下面的为例:

<div id="A" onClick={this.handleClick}>
button
<div>

我们知道React在渲染时,会将fiber的props关联到真实的dom的__reactProps$属性上,此时

div#A.__reactProps$ = {onClick: this.handleClick
}

当我们点击按钮时,会触发div#root上的事件监听器:

function onclick(e){const target = e.targetconst fiberProps = target.__reactProps$const clickhandle = fiberProps.onClickif(clickhandle){clickhandle(e)}
}

这样我们就可以实现事件的委托。这其中最重要的就是将fiber的props挂载到真实的dom实例的__reactProps$属性上。因此,只要我们在hydrate阶段能够成功关联dom和fiber,就自然也实现了事件的“绑定”

hydrate 源码剖析

hydrate 的过程发生在 render 阶段,commit 阶段几乎没有和 hydrate 相关的逻辑。render 阶段又分为两个小阶段:beginWorkcompleteUnitOfWork。只有HostRootHostComponentHostText三种类型的 fiber 节点才需要 hydrate,因此源码只针对这三种类型的 fiber 节点剖析

beginWork

beginWork 阶段判断 fiber 和 dom 实例是否满足混合的条件,如果满足,则为 fiber 关联 dom 实例:fiber.stateNode = dom

function beginWork(current, workInProgress, renderLanes) {switch (workInProgress.tag) {case HostRoot:return updateHostRoot(current, workInProgress, renderLanes);case HostComponent:return updateHostComponent(current, workInProgress, renderLanes);case HostText:return updateHostText(current, workInProgress);}
}

HostRoot Fiber

HostRoot fiber 是容器root的 fiber 节点。

这里主要是判断当前 render 是ReactDOM.render还是ReactDOM.hydrate,我们调用ReactDOM.hydrate渲染时,root.hydrate为 true。

如果是调用的ReactDOM.hydrate,则调用enterHydrationState函数进入hydrate的过程。这个函数主要是初始化几个全局变量:

  • isHydrating。表示当前正处于 hydrate 的过程。如果当前节点及其所有子孙节点都不满足 hydrate 的条件时,这个变量为 false
  • hydrationParentFiber。当前混合的 fiber。正常情况下,该变量和HostComponent或者HostText类型的 workInProgress 一致。
  • nextHydratableInstance。下一个可以混合的 dom 实例。当前 dom 实例的第一个子元素或者兄弟元素。

注意getNextHydratable会判断 dom 实例是否是ELEMENT_NODE类型(对应的 fiber 类型是HostComponent)或者TEXT_NODE类型(对应的 fiber 类型是HostText)。只有ELEMENT_NODE或者HostText类型的 dom 实例才是可以 hydrate 的

function updateHostRoot(current, workInProgress, renderLanes) {if (root.hydrate && enterHydrationState(workInProgress)) {var child = mountChildFibers(workInProgress, null, nextChildren);}return workInProgress.child;
}
function getNextHydratable(node) {// 跳过 non-hydratable 节点.for (; node != null; node = node.nextSibling) {var nodeType = node.nodeType;if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {break;}}return node;
}function enterHydrationState() {var parentInstance = fiber.stateNode.containerInfo;nextHydratableInstance = getNextHydratable(parentInstance.firstChild);hydrationParentFiber = fiber;isHydrating = true;
}

HostComponent

function updateHostComponent(current, workInProgress, renderLanes) {if (current === null) {tryToClaimNextHydratableInstance(workInProgress);}reconcileChildren(current, workInProgress, nextChildren, renderLanes);return workInProgress.child;
}

HostText Fiber

function updateHostText(current, workInProgress) {if (current === null) {tryToClaimNextHydratableInstance(workInProgress);}return null;
}

tryToClaimNextHydratableInstance

假设当前 fiberA 对应位置的 dom 为 domA,tryToClaimNextHydratableInstance 会首先调用tryHydrate判断 fiberA 和 domA 是否满足混合的条件:

  • 如果 fiberA 和 domA 满足混合的条件,则将hydrationParentFiber = fiberA;。并且获取 domA 的第一个子元素赋值给nextHydratableInstance
  • 如果 fiberA 和 domA 不满足混合的条件,则获取 domA 的兄弟节点,即 domB,调用tryHydrate判断 fiberA 和 domB 是否满足混合条件:
    • 如果 domB 满足和 fiberA 混合的条件,则将 domA 标记为删除,并获取 domB 的第一个子元素赋值给nextHydratableInstance
    • 如果 domB 不满足和 fiberA 混合的条件,则调用insertNonHydratedInstance提示错误:“Warning: Expected server HTML to contain a matching”,同时将isHydrating标记为 false 退出。

这里可以看出,tryToClaimNextHydratableInstance最多比较两个 dom 节点,如果两个 dom 节点都无法满足和 fiberA 混合的条件,则说明当前 fiberA 及其所有的子孙节点都无需再进行混合的过程,因此将isHydrating标记为 false。等到当前 fiberA 节点及其子节点都完成了工作,即都执行了completeWorkisHydrating才会被设置为 true,以便继续比较 fiberA 的兄弟节点

这里还需要注意一点,如果两个 dom 都无法满足和 fiberA 混合,那么nextHydratableInstance依然保存的是 domA,domA 会继续和 fiberA 的兄弟节点比对。

function tryToClaimNextHydratableInstance(fiber) {if (!isHydrating) {return;}var nextInstance = nextHydratableInstance;var firstAttemptedInstance = nextInstance;if (!tryHydrate(fiber, nextInstance)) {// 如果第一次调用tryHydrate发现当前fiber和dom不满足hydrate的条件,则获取dom的兄弟节点// 然后调用 tryHydrate 继续对比fiber和兄弟节点是否满足混合nextInstance = getNextHydratableSibling(firstAttemptedInstance);if (!nextInstance || !tryHydrate(fiber, nextInstance)) {// 对比了两个dom发现都无法和fiber混合,因此调用insertNonHydratedInstance控制台提示错误insertNonHydratedInstance(hydrationParentFiber, fiber);isHydrating = false;hydrationParentFiber = fiber;return;}// 如果第一次tryHydrate不满足,第二次tryHydrate满足,则说明兄弟节点和当前fiber是可以混合的,此时需要删除当前位置的domdeleteHydratableInstance(hydrationParentFiber, firstAttemptedInstance);}hydrationParentFiber = fiber;nextHydratableInstance = getFirstHydratableChild(nextInstance);
}// 将dom实例保存在 fiber.stateNode上
function tryHydrate(fiber, nextInstance) {switch (fiber.tag) {case HostComponent: {if (nextInstance.nodeType === ELEMENT_NODE &&fiber.type.toLowerCase() === nextInstance.nodeName.toLowerCase()) {fiber.stateNode = nextInstance;return true;}return false;}case HostText: {var text = fiber.pendingProps;if (text !== "" && nextInstance.nodeType === TEXT_NODE) {fiber.stateNode = nextInstance;return true;}return false;}default:return false;}
}

completeUnitOfWork

completeUnitOfWork 阶段主要是给 dom 关联 fiber 以及 props:dom.__reactProps$ = fiber.pendingProps;dom.__reactFiber$ = fiber;同时对比fiber.pendingPropsdom.attributes的差异

function completeUnitOfWork(unitOfWork) {var completedWork = unitOfWork;do {var current = completedWork.alternate;var returnFiber = completedWork.return;next = completeWork(current, completedWork, subtreeRenderLanes);var siblingFiber = completedWork.sibling;if (siblingFiber !== null) {workInProgress = siblingFiber;return;}completedWork = returnFiber;workInProgress = completedWork;} while (completedWork !== null);
}
function completeWork(current, workInProgress, renderLanes) {switch (workInProgress.tag) {case HostRoot: {if (current === null) {var wasHydrated = popHydrationState(workInProgress);if (wasHydrated) {markUpdate(workInProgress);}}return null;}case HostComponent:// 第一次渲染if (current === null) {var _wasHydrated = popHydrationState(workInProgress);if (_wasHydrated) {// 如果存在差异的属性,则将fiber副作用标记为更新if (prepareToHydrateHostInstance(workInProgress)) {markUpdate(workInProgress);}} else {}}case HostText: {var newText = newProps;if (current === null) {var _wasHydrated2 = popHydrationState(workInProgress);if (_wasHydrated2) {if (prepareToHydrateHostTextInstance(workInProgress)) {markUpdate(workInProgress);}}}return null;}}
}

popHydrationState

function popHydrationState(fiber) {if (fiber !== hydrationParentFiber) {return false;}if (!isHydrating) {popToNextHostParent(fiber);isHydrating = true;return false;}var type = fiber.type;if (fiber.tag !== HostComponent ||!shouldSetTextContent(type, fiber.memoizedProps)) {var nextInstance = nextHydratableInstance;while (nextInstance) {deleteHydratableInstance(fiber, nextInstance);nextInstance = getNextHydratableSibling(nextInstance);}}popToNextHostParent(fiber);nextHydratableInstance = hydrationParentFiber? getNextHydratableSibling(fiber.stateNode): null;return true;
}

以下图为例:

在这里插入图片描述

在 beginWork 阶段对 p#B fiber 工作时,发现 dom 树中同一位置的h1#B不满足混合的条件,于是继续对比h1#B的兄弟节点,即div#C,仍然无法混合,经过最多两轮对比后发现p#B这个 fiber 没有可以混合的 dom 节点,于是将 isHydrating 标记为 false,hydrationParentFiber = fiberP#Bp#B的子孙节点都不再进行混合的过程。

div#B1fiber 没有子节点,因此它可以调用completeUnitOfWork完成工作,completeUnitOfWork 阶段调用 popHydrationState 方法,在popHydrationState方法内部,首先判断 fiber !== hydrationParentFiber,由于此时的hydrationParentFiber等于p#B,因此条件成立,不用往下执行。

由于p#B fiber 的子节点都已经完成了工作,因此它也可以调用completeUnitOfWork完成工作。同样的,在popHydrationState函数内部,第一个判断fiber !== hydrationParentFiber不成立,两者是相等的。第二个条件!isHydrating成立,进入条件语句,首先调用popToNextHostParenthydrationParentFiber设置为p#B的第一个类型为HostComponent的祖先元素,这里是div#A fiber,然后将isHydrating设置为 true,指示可以为p#B的兄弟节点进行混合。

如果服务端返回的 DOM 有多余的情况,则调用deleteHydratableInstance将其删除,比如下图中div#D节点将会在div#Afiber 的completeUnitOfWork阶段删除

在这里插入图片描述

prepareToHydrateHostInstance

对于HostComponent类型的fiber会调用这个方法,这里只要是关联 dom 和 fiber:

  • 设置domInstance.__reactFiber$w63z5ormsqk = fiber
  • 设置domInstance.__reactProps$w63z5ormsqk = props
  • 对比服务端和客户端的属性
function prepareToHydrateHostInstance(fiber) {var domInstance = fiber.stateNode;var updatePayload = hydrateInstance(domInstance,fiber.type,fiber.memoizedProps,fiber);fiber.updateQueue = updatePayload;if (updatePayload !== null) {return true;}return false;
}
function hydrateInstance(domInstance, type, props, fiber) {precacheFiberNode(fiber, domInstance); // domInstance.__reactFiber$w63z5ormsqk = fiberupdateFiberProps(domInstance, props); // domInstance.__reactProps$w63z5ormsqk = props// 比较dom.attributes和props的差异,如果dom.attributes的属性比props多,说明服务端添加了额外的属性,此时控制台提示。// 注意,在对比过程中,只有服务端和客户端的children属性(即文本内容)不同时,控制台才会提示错误,同时在commit阶段,客户端会纠正这个错误,以客户端的文本为主。// 但是,如果是id不同,则客户端并不会纠正。return diffHydratedProperties(domInstance, type, props);
}

这里重点讲下diffHydratedProperties,以下面的demo为例:

// 服务端对应的dom
<div id="root"><div extra="server attr" id="server">客户端的文本</div></div>
// 客户端
render() {const { count } = this.state;return <div id="client">客户端的文本</div>;
}

diffHydratedProperties的过程中发现,服务端返回的id和客户端的id不同,控制台提示id不匹配,但是客户端并不会纠正这个,可以看到浏览器的id依然是server

同时,服务端多返回了一个extra属性,因此需要控制台提示,但由于已经提示了id不同的错误,这个错误就不会提示。

最后,客户端的文本和服务端的children不同,即文本内容不同,也需要提示错误,同时,客户端会纠正这个文本,以客户端的为主。

在这里插入图片描述

prepareToHydrateHostTextInstance

对于HostText类型的fiber会调用这个方法,这个方法逻辑比较简单,就不详细介绍了
务端对应的dom

<div id="root"><div extra="server attr" id="server">客户端的文本</div></div>
// 客户端
render() {const { count } = this.state;return <div id="client">客户端的文本</div>;
}

diffHydratedProperties的过程中发现,服务端返回的id和客户端的id不同,控制台提示id不匹配,但是客户端并不会纠正这个,可以看到浏览器的id依然是server

同时,服务端多返回了一个extra属性,因此需要控制台提示,但由于已经提示了id不同的错误,这个错误就不会提示。

最后,客户端的文本和服务端的children不同,即文本内容不同,也需要提示错误,同时,客户端会纠正这个文本,以客户端的为主。

在这里插入图片描述

prepareToHydrateHostTextInstance

对于HostText类型的fiber会调用这个方法,这个方法逻辑比较简单,就不详细介绍了

相关文章:

从源码角度看React-Hydrate原理

React 渲染过程&#xff0c;即ReactDOM.render执行过程分为两个大的阶段&#xff1a;render 阶段以及 commit 阶段。React.hydrate渲染过程和ReactDOM.render差不多&#xff0c;两者之间最大的区别就是&#xff0c;ReactDOM.hydrate 在 render 阶段&#xff0c;会尝试复用(hydr…...

ARM基础 -- 2

文章目录一、可编程器件的编程原理1.1 电子器件的发展方向1.2 可编程器件的特点1.3 整个编程及运行过程二、指令集对CPU的意义2.1 汇编语言与C等高级语言的差异2.2 汇编语言的本质2.2.1 编程语言的发展过程2.2.2 汇编语言的特点和用途三、RISC和CISC的区别3.1 复杂指令集CPU --…...

Java 类型转换

Java 类型转换 int转Integer int int0 1; Integer integer1 int0; // 自动装箱 Integer integer2 new Integer(int0); Integer integer3 Integer.valueOf(int0);Integer转int Integer integer0 2; int int1 integer0; // 自动拆箱 int int2 integer0.intValue(); // …...

【Java开发】JUC基础 05:线程通信/协作

1 生产者消费者问题&#x1f4cc; 线程通信应用的场景可以简单地描述为生产者和消费者问题假设仓库中只能存放一件产品&#xff0c;生产者将生产出来的产品放入仓库&#xff0c;消费者将仓库中产品取走消费&#xff1b;如果仓库中没有产品&#xff0c;则生产者将产品放入仓库&a…...

哪些工具可以实现在线ps的需求

在线Photoshop有哪些工具可以选择&#xff1f;在 Adobe 的官网上就能够实现&#xff0c;很惊讶吧&#xff0c;其实 Adobe 官方推出了在线版本的 Photoshop 的&#xff0c;尽管目前还是 Beta版本&#xff0c;但其实也开放了蛮久了。编辑切换为居中添加图片注释&#xff0c;不超过…...

如何使用C2concealer生成随机化的C2 Malleable配置文件

关于C2concealer C2concealer是一款功能强大的命令行工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松生成随机化的C2 Malleable配置文件&#xff0c;以便在Cobalt Strike中使用。 工具运行机制 开发人员对Cobalt Strike文档进行了详细的研究&#xff0c;…...

网络基础之IP地址和子网掩码

一、IP地址IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一个逻辑地址&#xff0c;以此来屏蔽物理地址的差异。习惯上&#xff0c;我们用分成四段的十进制数表示IP地址&#xff0c;从0.0.0.0 一直到255.255.255.255。互联网上的…...

G1D54-CRF

一、CRF的输入X是什么&#xff1f;是构造的特征吗&#xff1f; 如此&#xff0c;CRF的x只用于状态函数吗&#xff1f; CRF的例子解释调用代码 机器之心 知乎忆榛 此处线性链条件随机场的特征函数形式被统一了&#xff1f; BilstmCRF&#xff0c;强烈推荐&#xff01;&#x…...

vue3 使用defineAsyncComponent与component标签实现动态渲染组件

内容有些啰嗦&#xff0c;内容记载了当时遇到了bug以及解决问题的思路。 业务场景简述&#xff1a; 前端做配置化组件&#xff0c;通过url内的唯一标识&#xff0c;请求后端sql 哪取页面配置信息&#xff0c;前端通过配置信息动态渲染查询表单&#xff0c;导出、表格&#xff…...

Linux下 C/C++ NTP网络时间协议详解

NTP&#xff08;Network Time Protocol&#xff0c;网络时间协议&#xff09;是由RFC 1305定义的时间同步协议。它是通过网络在计算机系统之间进行时钟同步的网络协议。NTP 在公共互联网上通常能够保持时间延迟在几十毫秒以内的精度&#xff0c;并在理想条件下&#xff0c;它能…...

Pytest自动化框架-权威教程02-Pytest 使用及调用方法

Pytest 使用及调用方法使用python -m pytest调用pytest2.0版本新增你可以在命令行中通过Python编译器来调用Pytest执行测试:Copypython -m pytest [...]通过python调用会将当前目录也添加到sys.path中,除此之外,这几乎等同于命令行直接调用pytest [...]。可能出现的执行退出cod…...

大数据技术——概述

根据IBM前首席执行官郭士纳的观点&#xff0c;IT领域每隔十五年就会迎来一次重大变革三次信息化浪潮1.存储设备容量不断增加2.CPU处理能力大幅提升3.网络带宽不断增加运营式系统阶段数据库的出现使得数据管理的复杂度大大降低,数据往往伴随着一定的运营活动而产生并记录在数据库…...

java-代理模式

背景 代理模式指的是提供一个代理对象用于访问目标对象,可以很方便的在不修改目标对象的情况下,提供额外的功能,扩展目标对象。 case1:静态代理 约束:代理对象和目标对象要实现相同的接口 优点:不修改目标对象的情况下扩展功能 缺点:必须实现相同的接口,如果接口发生变…...

路由网络的构建与配置

Part.1 ⑴ 需求分析 在构建的局域网中&#xff0c;通过路由器间配置静态路由&#xff0c;实现PC1和PC2主机直接连通&#xff0c;主机网段不能与路由器直接互联网段通信。 ⑵ 环境要求 配置虚拟网卡的计算机&#xff0c;安装华为eNSP模拟软件。 规划拓扑 Part.2 ⑴ 拓扑描述…...

软件测试-接口测试-数据库管理

文章目录 1.数据库介绍2.数据库基本操作2.1安装2.2 操作流程2.3数据准备2.4数据的基本操作2.4.1 连接数据库并查询数据库版本2.4.2 连接数据库执行数据库查询操作2.4.3 连接数据库执行数据库插入操作2.4.4 连接数据库执行数据库更新操作3.数据库事务操作3.1 案例:数据不一致性…...

【华为OD机试 】天然蓄水库(C++ Java JavaScript Python)

文章目录 题目描述输入描述输出描述备注用例题目解析C++JavaScriptJavaPython题目描述 公元2919年,人类终于发现了一颗宜居星球——X星。 现想在X星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大? 要求: 山脉用正整数数组s表示,每个元素代表山脉…...

普元EOS中导出excl页面下载

起因 需要做一个筛选功能的导出表格 解决办法 这个垃圾eos我是真受不了,sb玩意的缺点三天三夜也说不完 后边就没法整response的这些个东西,可真是够愁人的 在网上搜了搜 在普元的帮助文档里也看了看 普元提供的像是老太太的裹脚布一般又臭又长 参照这个可以看一下...

内存的管理

取指令——译码——执行——返存 计组课我们学过cpu真正读指令并非是从内存中读入&#xff0c;而是从cache读和存&#xff0c;再由cache进行取指或返存&#xff0c;因为cpu指令周期比内存周期速度快很多&#xff0c;cpu若要取指或返存都需要等待内存完成他的动作才可以进行下一…...

OpenFeign 切换HttpClient遇到的问题

背景 OpenFeign支持三种Http请求方式&#xff0c;默认情况下通过jdk中的HttpURLConnection向下游服务发起http请求&#xff08;详见下图&#xff0c;源码详见feign.Client.Default&#xff09;&#xff0c; 默认的Client 采用 HttpURLConnection&#xff0c; 这种是无法复用的…...

流计算框架storm概览

Attention: supervison 和 nimbus的状态都实时保存在zookeeper集群中和本地. Enchance, this means you can kill -9 Nimbus or the Supervisors and theyll start back up as nothing happened. Topologies 1. storm jar all-my-code.jar org.apache.storm.MyTopology a…...

如何使用Coercer强制Windows Server认证任意主机

关于Coercer Coercer是一款功能强大的Python脚本&#xff0c;该工具可以通过九种不同的方法来强制让一台Windows Server认证任意主机。 功能介绍 1、自动检测远程设备的开放SMP管道&#xff1b; 2、一一调用存在安全漏洞的RPC功能来强制一台Windows Server认证任意主机&#…...

【小程序】已有公众号认证,一步一步申请小程序(图文)

一、登陆公众号后台&#xff0c;找到左侧广告与服务&#xff0c;小程序管理&#xff0c;开通 二、选择快速注册认证小程序 三、快速创建 四、选择微信认证资质&#xff08;复用&#xff09;&#xff0c;这样不用再付认证费了 五、需要一个新的邮箱&#xff0c;这点挺让人无语&a…...

Redis学习笔记:缓存运用常见问题

这是本人学习的总结&#xff0c;主要学习资料如下 马士兵教育 目录1、数据一致性的问题1.1、新增数据一致性的问题1.2、修改/删除一致性问题1.2.1、操作分析1.2.1、总结和再深入2、缓存穿透&#xff0c;缓存击穿和缓存雪崩2.1、缓存穿透&#xff08;查不到&#xff09;2.1.1、…...

使用python 脚本挑出coco 数据集中的某一类数据

文章大纲 简介代码样例制作一个走路玩手机数据集简介 MS COCO的全称是Microsoft Common Objects in Context,起源于微软于2014年出资标注的Microsoft COCO数据集,与ImageNet竞赛一样,被视为是计算机视觉领域最受关注和最权威的比赛之一。 COCO数据集是一个大型的、丰富的物…...

Python虚拟环境(pipenv、venv、conda一网打尽)[通俗易懂]

一、什么是虚拟环境 1. 什么是Python环境 要搞清楚什么是虚拟环境&#xff0c;首先要清楚Python的环境指的是什么。当我们在执行python test.py时&#xff0c;思考如下问题&#xff1a; python哪里来&#xff1f;这个主要归功于配置的系统环境变量PATH&#xff0c;当我们在命…...

Android Kotlin实战之高阶使用泛型扩展协程懒加载详解

前言&#xff1a; 通过前面几篇文章&#xff0c;我们已基本掌握kotlin的基本写法与使用&#xff0c;但是在开发过程中&#xff0c;以及一些开源的API还是会出现大家模式的高阶玩法以及问题&#xff0c;如何避免&#xff0c;接下来讲解针对原来的文章进行一些扩展&#xff0c;解…...

数字映射:数字孪生技术的应用场景及作用

对于许多行业来说&#xff0c;数字孪生技术是未来。数字孪生定义数字孪生不仅仅是某物的副本或克隆&#xff0c;它是对象或系统的动态实时表示。数字孪生是一种虚拟模型&#xff0c;旨在准确反映物理对象。是物理对象、流程、服务或环境的数字表示&#xff0c;其行为和外观与现…...

配置二层远程端口镜像案例

实验拓扑&#xff1a; 实验需求&#xff1a; 如图1所示&#xff0c;某公司行政部通过SwitchA与外部Internet通信&#xff0c;监控设备Server通过SwitchB与SwitchA相连。 现在希望Server能够远程对行政部访问Internet的流量进行监控。 操作步骤&#xff1a; 配置观察端口 # 在…...

Linux-0.11 kernel目录fork.c详解

Linux-0.11 kernel目录fork.c详解 fork.c中主要实现内核对于创建新的进程的行为。其中copy_process是其最核心的函数。 copy_process int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,long ebx,long ecx,long edx,long fs,long es,long ds,long eip,…...

如何或者无插件Web页面监控播放软件LiveNVR的固定视频流地址,实现大屏上墙、播放、视频分析等目的

1、LiveNVR介绍 LiveNVR的安防监控的视频直播&#xff0c;可以按标准的Onvif/RTSP协议接入监控设备&#xff0c;也可以通过海康、大华、天地伟业等厂家私有SDK接入监控&#xff0c;实现web页面的播放和录像回放。 可以分发HTTP-FLV、WS-FLV、WebRTC、RTMP、HLS(M3U8)、RTSP等多…...

北京做网站s/深圳网站优化排名

代码实现报表打印 //初始化报表信息 private void SetReportInfo(string reportPath,string sourceName,DataTable dataSource,bool isFengPi) {if (!File.Exists(reportPath)) { MessageBox.Show("报表文件:" reportPath " 不存在!","提示&…...

昆明公司建设网站制作/重庆百度推广关键词优化

场景一&#xff1a;分流原因&#xff1a;服务器有2块或多块场景二&#xff1a;网维大师与游戏虚拟盘分别使用独立的服务器。分流原因&#xff1a;因为客户机较多&#xff0c;所以虚拟盘服务器用了2块网卡&#xff0c;希望将同步节点的流量与虚拟盘的流量分流。来避免同步节点工…...

广西高端网站建设/怎么做网站推广多少钱

myeclipse 遇到的一些问题及解决方案参考文章&#xff1a; &#xff08;1&#xff09;myeclipse 遇到的一些问题及解决方案 &#xff08;2&#xff09;https://www.cnblogs.com/losesea/p/3417291.html &#xff08;3&#xff09;https://www.javazxz.com/thread-4655-1-1.h…...

手机网站开发者模式/什么是整合营销概念

阿里篇&#xff08;仅有问题&#xff0c;没有答案需要大家共同学习探讨&#xff09; 如何实现一个高效的单向链表逆序输出&#xff1f;已知 sqrt (2)约等于 1.414&#xff0c;要求不用数学库&#xff0c;求 sqrt (2)精确到小数点后 10 位。给定一个二叉搜索树(BST)&#xff0c…...

做视频教育网站/无锡seo公司找哪家好

HTTPS和HTTP的区别主要如下&#xff1a; 1、https协议需要到ca申请证书&#xff0c;一般免费证书较少&#xff0c;因而需要一定费用。 2、http是超文本传输协议&#xff0c;信息是明文传输&#xff0c;https则是具有安全性的ssl加密传输协议。 3、http和https使用的是完全不…...

网站备案有期限吗/百度知道合伙人答题兼职

2019独角兽企业重金招聘Python工程师标准>>> 参考链接&#xff1a; http://blog.coinidea.com/web%E5%BC%80%E5%8F%91/nodejs-1131.html 由于nodejs是非阻塞单进程单线程的&#xff0c;一旦nodejs抛出异常&#xff0c;整个服务就会停掉。服务将会非常不稳定。 解决方…...