React16源码: React中创建更新的方式及ReactDOM.render的源码实现
React当中创建更新的主要方式
- ReactDOM.render || hydrate
- 这两个API都是我们要把整个应用第一次进行渲染到我们的页面上面
- 能够展现出来我们整个应用的样子的一个过程
- 这是初次渲染
- setState
- 后续更新应用
- forceUpdate
- 后续更新应用
replaceState- 在后续被舍弃
关于 ReactDOM.render
1 )概述
- 它先要去创建一个
ReactRoot,这是一个包含react它整个应用的一个最顶点的一个对象 - 之后是创建
FiberRoot和RootFiber - 第三步,就是要创建一个更新,创建更新之后,应用就可以进入到一个更新调度的阶段
- 在进入调度之后,不管是通过
setState还是ReactDOM.render - 它们后续的操作都是由调度器去管理的,跟我们实际调用的API就已经没有任何关系了
- 也就是后面都交给内部调度器来掌控全局,这块先不涉及调度相关内容
- 在进入调度之后,不管是通过
2 )Demo 示例
App.js
import React, { Component } from 'react'
import './App.css'class List extends Component {state = {a: 1,b: 2,c: 3,}handleClick = () => {this.setState(oldState => {const { a, b, c } = oldStatereturn {a: a * a,b: b * b,c: c * c,}})}render() {const { a, b, c } = this.statereturn [<span key="a">{a}</span>,<span key="b">{b}</span>,<span key="c">{c}</span>,<button key="button" onClick={this.handleClick}>click me</button>,]}
}class Input extends Component {state = {name: 'wang',}handleChange = e => {// 这里如果使用方法设置`state`// 那么需要现在外面读取`e.target.value`// 因为在React走完整个事件之后会重置event对象// 以复用event对象,如果等到方法被调用的时候再读取`e.target.value`// 那时`e.target`是`null`this.setState({name: e.target.value,})}render() {return (<inputtype="text"style={{ color: 'red' }}onChange={this.handleChange}value={this.state.name}/>)}
}class App extends Component {render() {return (<div className="main"><Input /><List /></div>)}
}export default App
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './demos/lazy'ReactDOM.render(<App />, document.getElementById('root'))
- 这个demo非常的简单, 它首先有一个App, 它 render 了两个子节点,一个是
Input,一个是List - 最终渲染应用
ReactDOM.render(<App />, document.getElementById('root'))- 应用渲染出来之后,会挂载到这个root的Dom节点上面
这个 React App 小程序通过 ReactElement 形成一个树结构
App|render() return|div/ \/ \children[0] children[1]/ \/ \/ \Input List| \
render() return render() return | \input (span span span button)
- 我们传入的App是一个class component,它调用render之后会得到一个div标签
- 这个div标签就是它的children, 也是它 return 的唯一一个节点
- 这个节点它有两个children,一个是Input组件,另外一个是List组件
- 这个Input组件它调用render之后会返回一个input,就是原生的input标签
- 这个List组件它调用render之后 return 的是一个数组
- 这个数组里面有三个span标签和一个button标签
- 这就是整个树结构,我们完全可以通过一些特定的方式去获取到它的相应的子节点,一层一层下来
3 )特别说明
ReactDOM.render(<App />, document.getElementById('root'))这个写法中的<App />- 实际上是调用
React.createElement传递进去的是 App 这个类,但并没有去创建它的一个实例 - 这个时候,我们还什么东西都没有,因为我们只得到了一个ReactElement
- 最终我们要形成一个把页面渲染出来的过程是
ReactDOM.render这个方法,它接下去要做的事情
4 )源码解析
-
ReactDOM.js 链接: https://github.com/facebook/react/blob/v16.6.0/packages/react-dom/src/client/ReactDOM.js
-
在react-dom 下面会有很多不同的包,比如 client, server, shared, 对应的就是渲染平台不一样
-
server是在 nodejs 平台进行渲染的它的一个工具包, 我们把精力放在 client 下面
-
在react-dom里面先找到定义的 ReactDOM 对象
const ReactDOM: Object = {createPortal,findDOMNode(componentOrElement: Element | ?React$Component<any, any>,): null | Element | Text {if (__DEV__) {let owner = (ReactCurrentOwner.current: any);if (owner !== null && owner.stateNode !== null) {const warnedAboutRefsInRender =owner.stateNode._warnedAboutRefsInRender;warningWithoutStack(warnedAboutRefsInRender,'%s is accessing findDOMNode inside its render(). ' +'render() should be a pure function of props and state. It should ' +'never access something that requires stale data from the previous ' +'render, such as refs. Move this logic to componentDidMount and ' +'componentDidUpdate instead.',getComponentName(owner.type) || 'A component',);owner.stateNode._warnedAboutRefsInRender = true;}}if (componentOrElement == null) {return null;}if ((componentOrElement: any).nodeType === ELEMENT_NODE) {return (componentOrElement: any);}if (__DEV__) {return DOMRenderer.findHostInstanceWithWarning(componentOrElement,'findDOMNode',);}return DOMRenderer.findHostInstance(componentOrElement);},hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {// TODO: throw or warn if we couldn't hydrate?return legacyRenderSubtreeIntoContainer(null,element,container,true,callback,);},render(element: React$Element<any>,container: DOMContainer,callback: ?Function,) {return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);},unstable_renderSubtreeIntoContainer(parentComponent: React$Component<any, any>,element: React$Element<any>,containerNode: DOMContainer,callback: ?Function,) {invariant(parentComponent != null && ReactInstanceMap.has(parentComponent),'parentComponent must be a valid React Component',);return legacyRenderSubtreeIntoContainer(parentComponent,element,containerNode,false,callback,);},unmountComponentAtNode(container: DOMContainer) {invariant(isValidContainer(container),'unmountComponentAtNode(...): Target container is not a DOM element.',);if (container._reactRootContainer) {if (__DEV__) {const rootEl = getReactRootElementInContainer(container);const renderedByDifferentReact =rootEl && !ReactDOMComponentTree.getInstanceFromNode(rootEl);warningWithoutStack(!renderedByDifferentReact,"unmountComponentAtNode(): The node you're attempting to unmount " +'was rendered by another copy of React.',);}// Unmount should not be batched.DOMRenderer.unbatchedUpdates(() => {legacyRenderSubtreeIntoContainer(null, null, container, false, () => {container._reactRootContainer = null;});});// If you call unmountComponentAtNode twice in quick succession, you'll// get `true` twice. That's probably fine?return true;} else {if (__DEV__) {const rootEl = getReactRootElementInContainer(container);const hasNonRootReactChild = !!(rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl));// Check if the container itself is a React root node.const isContainerReactRoot =container.nodeType === ELEMENT_NODE &&isValidContainer(container.parentNode) &&!!container.parentNode._reactRootContainer;warningWithoutStack(!hasNonRootReactChild,"unmountComponentAtNode(): The node you're attempting to unmount " +'was rendered by React and is not a top-level container. %s',isContainerReactRoot? 'You may have accidentally passed in a React root node instead ' +'of its container.': 'Instead, have the parent component update its state and ' +'rerender in order to remove this component.',);}return false;}},// Temporary alias since we already shipped React 16 RC with it.// TODO: remove in React 17.unstable_createPortal(...args) {if (!didWarnAboutUnstableCreatePortal) {didWarnAboutUnstableCreatePortal = true;lowPriorityWarning(false,'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +'and will be removed in React 17+. Update your code to use ' +'ReactDOM.createPortal() instead. It has the exact same API, ' +'but without the "unstable_" prefix.',);}return createPortal(...args);},unstable_batchedUpdates: DOMRenderer.batchedUpdates,unstable_interactiveUpdates: DOMRenderer.interactiveUpdates,flushSync: DOMRenderer.flushSync,unstable_flushControlled: DOMRenderer.flushControlled,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {// Keep in sync with ReactDOMUnstableNativeDependencies.js// and ReactTestUtils.js. This is an array for better minification.Events: [ReactDOMComponentTree.getInstanceFromNode,ReactDOMComponentTree.getNodeFromInstance,ReactDOMComponentTree.getFiberCurrentPropsFromNode,EventPluginHub.injection.injectEventPluginsByName,EventPluginRegistry.eventNameDispatchConfigs,EventPropagators.accumulateTwoPhaseDispatches,EventPropagators.accumulateDirectDispatches,ReactControlledComponent.enqueueStateRestore,ReactControlledComponent.restoreStateIfNeeded,ReactDOMEventListener.dispatchEvent,EventPluginHub.runEventsInBatch,],}, }; -
这个对象里面,有一个
render方法,它接收3个参数- 一个是
element,本质是 React$Element 对象 - 第二个是
container,就是我们要挂载到哪个dom节点上面 - 第三个是
callback, 就是说这个应用它渲染结束之后,它会调用这个callback
- 一个是
-
render方法最终 return 了一个legacyRenderSubtreeIntoContainer方法- 传入了
null,element,container,false,callback四个方法 - 主要看下 第一个
null和 第四个false
- 传入了
-
现在定位到
legacyRenderSubtreeIntoContainer这个方法function legacyRenderSubtreeIntoContainer(parentComponent: ?React$Component<any, any>,children: ReactNodeList,container: DOMContainer,forceHydrate: boolean,callback: ?Function, ) {// TODO: Ensure all entry points contain this checkinvariant(isValidContainer(container),'Target container is not a DOM element.',);if (__DEV__) {topLevelUpdateWarnings(container);}// TODO: Without `any` type, Flow says "Property cannot be accessed on any// member of intersection type." Whyyyyyy.let root: Root = (container._reactRootContainer: any);if (!root) {// Initial mountroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate,);if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);originalCallback.call(instance);};}// Initial mount should not be batched.DOMRenderer.unbatchedUpdates(() => {if (parentComponent != null) {root.legacy_renderSubtreeIntoContainer(parentComponent,children,callback,);} else {root.render(children, callback);}});} else {if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);originalCallback.call(instance);};}// Updateif (parentComponent != null) {root.legacy_renderSubtreeIntoContainer(parentComponent,children,callback,);} else {root.render(children, callback);}}return DOMRenderer.getPublicRootInstance(root._internalRoot); } -
传进来的第一个参数
null它对应的是叫做parentComponent这么一个参数 -
接着往下,它定义一个
root, 即:let root: Root = (container._reactRootContainer: any);- 获取是否有
_reactRootContainer这个属性 - 正常来讲,一个普通的dom对象,肯定不会有这种属性在上面的
- 所以第一次渲染的时候,它肯定是不存在的
- 所以我们主要关心的就是下面if里面的这个条件满足的root不存在的情况
- 获取是否有
-
如果root不存在,则进行创建
// Initial mount root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container,forceHydrate, ); -
这个方法
legacyCreateRootFromDOMContainer我们也要注意,它接受两个参数container: DOMContainerforceHydrate: boolean- 我们在函数调用栈向上溯源,传进去的
forceHydrate是一个false,这是一开始就写死的 - 我们在最顶层对比,可知,在 626 行的
hydrate方法的第四个参数传递的是true - 因为
hydrate跟render方法本质是一样的,唯一的一个区别,就是是否会调和原来存在于这个dom节点 - 就是我们
container里面的它的HTML的节点, 是否要复用这些节点 - 它主要是在有服务端渲染的情况下会使用
hydrate这个API - 因为服务端渲染出来的情况,它里面的dom节点应该是跟客户端渲染的时候,第一次渲染,它得到的节点是一模一样的
- 这个时候如果可以复用这些dom节点,可以提高一定的性能
- 所以
hydrate跟render内部的唯一的区别就是传的第四个参数是true或false
- 我们在函数调用栈向上溯源,传进去的
-
再回到
legacyCreateRootFromDOMContainer这个函数function legacyCreateRootFromDOMContainer(container: DOMContainer,forceHydrate: boolean, ): Root {const shouldHydrate =forceHydrate || shouldHydrateDueToLegacyHeuristic(container);// First clear any existing content.if (!shouldHydrate) {let warned = false;let rootSibling;while ((rootSibling = container.lastChild)) {if (__DEV__) {if (!warned &&rootSibling.nodeType === ELEMENT_NODE &&(rootSibling: any).hasAttribute(ROOT_ATTRIBUTE_NAME)) {warned = true;warningWithoutStack(false,'render(): Target node has markup rendered by React, but there ' +'are unrelated nodes as well. This is most commonly caused by ' +'white-space inserted around server-rendered markup.',);}}container.removeChild(rootSibling);}}if (__DEV__) {if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {warnedAboutHydrateAPI = true;lowPriorityWarning(false,'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +'will stop working in React v17. Replace the ReactDOM.render() call ' +'with ReactDOM.hydrate() if you want React to attach to the server HTML.',);}}// Legacy roots are not async by default.const isConcurrent = false;return new ReactRoot(container, isConcurrent, shouldHydrate); }- 在
render函数中,进入了这个函数,forceHydrate参数的值就是false - 这边定义了一个
shouldHydrate来得到是否应该进行 Hydrate const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);function shouldHydrateDueToLegacyHeuristic(container) {const rootElement = getReactRootElementInContainer(container);return !!(rootElement &&rootElement.nodeType === ELEMENT_NODE &&rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME) // 这里的 ROOT_ATTRIBUTE_NAME 是 'data-reactroot' 老版本服务端渲染,会在第一个节点上加上这个标识); } function getReactRootElementInContainer(container: any) {if (!container) {return null;}// 判断节点类型是否是 DOCUMENT_NODE 类型if (container.nodeType === DOCUMENT_NODE) {return container.documentElement;} else {// 否则,返回第一个孩子节点return container.firstChild;} }- 通过判断有
rootElement这个节点,并且它有这个ROOT_ATTRIBUTE_NAME属性 - 来判断它是否需要进行一个合并: 老的html节点和我们客户端第一次渲染出来的应用的所有节点进行合并的一个过程
- 因为是跟服务端渲染相关的,跟整体的更新流程没有特别大的关系,所以就不是特别重要
- 再回到
shouldHydrate在下面的一个if (!shouldHydrate) {}判断中,没有服务端渲染,这里是false是会进入判断的 - 执行了一个 while循环,也就是循环删除 container 下面的所有子节点
- 因为这些子节点里面的东西不是在我们整个reactApp 渲染出来之后,还可以用的节点
- 因为我们不需要去合并它,所以就把这些节点全部删了
- 忽略下面 DEV 的判断,最终返回了一个 ReactRoot:
return new ReactRoot(container, isConcurrent, shouldHydrate);
- 在
-
接下来,进入
new ReactRoot的过程function ReactRoot(container: Container,isConcurrent: boolean,hydrate: boolean, ) {const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);this._internalRoot = root; }- 通过
const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);创建了一个 root 节点 - 而
DOMRenderer是在import * as DOMRenderer from 'react-reconciler/inline.dom';- 在 react-reconciler 这个包中的函数
- react-reconciler 是在 react 中非常重要的一个模块
- 它处理和平台无关的节点的调和操作和任务调度的操作
- react-reconciler 中的代码比 react-dom中的还要复杂
- 在 react-reconciler/inline.dom 文件下只有一行代码
export * from './src/ReactFiberReconciler'; - 打开这个 js 文件,找到
createContainerexport function createContainer(containerInfo: Container,isConcurrent: boolean,hydrate: boolean, ): OpaqueRoot {return createFiberRoot(containerInfo, isConcurrent, hydrate); } - 最终它创建了一个 FiberRoot, 这里先不展开
- 回到
ReactRoot, 它这边挂载了一个 _internalRoot,this._internalRoot = root;
- 通过
-
退回到
legacyCreateRootFromDOMContainer它最终返回了一个ReactRoot -
再退回到调用
legacyCreateRootFromDOMContainer的legacyRenderSubtreeIntoContainer函数中root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);- 接下来,判断是否有
callback, 没有则对其进行简单的封装处理if (typeof callback === 'function') {const originalCallback = callback;callback = function() {const instance = DOMRenderer.getPublicRootInstance(root._internalRoot);originalCallback.call(instance);}; } - 接着进入核心环节
DOMRenderer.unbatchedUpdates// Initial mount should not be batched. DOMRenderer.unbatchedUpdates(() => {if (parentComponent != null) {root.legacy_renderSubtreeIntoContainer(parentComponent,children,callback,);} else {root.render(children, callback);} }); unbatchedUpdates涉及到 react 中的一个概念batchedUpdates批量更新,这里先跳过- 可以理解为 改了 scheduler 里的一个全局变量,可以暂时忽略,这里涉及到更新的过程
- 然后
unbatchedUpdates里面的回调直接被执行,里面直接走 else,也就是执行root.render(children, callback); - 这里的
root.render方法, 实际上是ReactRoot.prototype.render = function(children: ReactNodeList,callback: ?() => mixed, ): Work {const root = this._internalRoot;const work = new ReactWork();callback = callback === undefined ? null : callback;if (__DEV__) {warnOnInvalidCallback(callback, 'render');}if (callback !== null) {work.then(callback);}DOMRenderer.updateContainer(children, root, null, work._onCommit);return work; }; - 这里创建了一个
ReactWork, 最终调用了DOMRenderer.updateContainer- 这里的
updateContainer是在 ReactFiberReconciler.js 中的export function updateContainer(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,callback: ?Function, ): ExpirationTime {const current = container.current;const currentTime = requestCurrentTime();const expirationTime = computeExpirationForFiber(currentTime, current);return updateContainerAtExpirationTime(element,container,parentComponent,expirationTime,callback,); } - 这里的第一个参数
element是上层ReactRoot.prototype.render的第一个参数,还可以向上继续溯源 - 这里最核心的是, 计算 expirationTime, 这里是React 16让我们使用
ConcurrentMode进行一个优先级的任务更新 - 这里
computeExpirationForFiber涉及一个非常复杂的计算过程,先跳过 - 最后调用
updateContainerAtExpirationTime来返回结果,进入这个方法export function updateContainerAtExpirationTime(element: ReactNodeList,container: OpaqueRoot,parentComponent: ?React$Component<any, any>,expirationTime: ExpirationTime,callback: ?Function, ) {// TODO: If this is a nested container, this won't be the root.const current = container.current;if (__DEV__) {if (ReactFiberInstrumentation.debugTool) {if (current.alternate === null) {ReactFiberInstrumentation.debugTool.onMountContainer(container);} else if (element === null) {ReactFiberInstrumentation.debugTool.onUnmountContainer(container);} else {ReactFiberInstrumentation.debugTool.onUpdateContainer(container);}}}const context = getContextForSubtree(parentComponent);if (container.context === null) {container.context = context;} else {container.pendingContext = context;}return scheduleRootUpdate(current, element, expirationTime, callback); } - 它获取了一个 context,
const context = getContextForSubtree(parentComponent);这个先忽略,因为 parentComponent 是 null - 下面的 if else 先忽略,简单认为
container.context和container.pendingContext都不存在 - 在 react-dom 的 api 中没有任何方法可以在root节点上提供context的入口,先忽略它们
- 最终
scheduleRootUpdate作为返回值,进入这个方法function scheduleRootUpdate(current: Fiber,element: ReactNodeList,expirationTime: ExpirationTime,callback: ?Function, ) {if (__DEV__) {if (ReactCurrentFiber.phase === 'render' &&ReactCurrentFiber.current !== null &&!didWarnAboutNestedUpdates) {didWarnAboutNestedUpdates = true;warningWithoutStack(false,'Render methods should be a pure function of props and state; ' +'triggering nested component updates from render is not allowed. ' +'If necessary, trigger nested updates in componentDidUpdate.\n\n' +'Check the render method of %s.',getComponentName(ReactCurrentFiber.current.type) || 'Unknown',);}}const update = createUpdate(expirationTime);// Caution: React DevTools currently depends on this property// being called "element".update.payload = {element};callback = callback === undefined ? null : callback;if (callback !== null) {warningWithoutStack(typeof callback === 'function','render(...): Expected the last optional `callback` argument to be a ' +'function. Instead received: %s.',callback,);update.callback = callback;}enqueueUpdate(current, update);scheduleWork(current, expirationTime);return expirationTime; }- 跳过里面的 DEV 判断的代码,它创建了一个 update
const update = createUpdate(expirationTime); - update 是用来标记 react 应用当中需要更新的地点的
- 接着设置 update 的一些属性,如:payload, callback
- 最后调用
enqueueUpdate, 是把 update 加入到我们这个Fiber对象上面对应的updateQueue里面 - update它是可以在一次更新当中,这个节点上面有多个更新的,
- 就是一个整体的react应用的更新过程当中会有很多次更新在某一个节点上产生
- 这跟
batchUpdates是有一定的关系的 - 最终调用
scheduleWork就是开始任务调度,告诉 react 有更新产生了,要进行更新了,也要开始调度了- 为何要调度?
- react 16之后,提供了一个任务优先级的概念, 因为有可能在同一时间, 有各种优先级的任务在应用里面
- 就需要有个调度器来进行指挥调度,按照优先级,先执行优先级高的任务,再执行优先级低的任务
- 这才是react更新中,最复杂的逻辑
- 跳过里面的 DEV 判断的代码,它创建了一个 update
- 这里的
简单总结
- 在
ReactDOM.render过程当中,创建了一个ReactRoot - 同时在
ReactRoot创建的过程中创建了FiberRoot FiberRoot在创建的过程中也会自动去初始化一个 Fiber 对象(上面暂没有涉及)- 然后又在这个 root 上面去创建了一个
expirationTime - 之后又创建了一个 update 这个更新的对象,然后把这个更新的对象放到我们的 root 的节点上面
- 之后就进入了一个更新的过程, 这就是一个创建更新的过程
- 创建完更新, 再去实际的调度整个任务的更新
相关文章:
React16源码: React中创建更新的方式及ReactDOM.render的源码实现
React当中创建更新的主要方式 ReactDOM.render || hydrate 这两个API都是我们要把整个应用第一次进行渲染到我们的页面上面能够展现出来我们整个应用的样子的一个过程这是初次渲染 setState 后续更新应用 forceUpdate 后续更新应用 replaceState 在后续被舍弃 关于 ReactDOM…...
CentOS 7 系列默认的网卡接口名称
CentOS 7 系列默认的网卡接口是随机的,如果要修改网卡名称以 eth 开头,有两种方式。 方法一:安装系统时 在安装界面移动光标到 Install Centos 7.按 TAB 键 在出现的代码的末尾添加:net.ifnames0 biosdevname0.按下回车开始安装即…...
多文件上传
HTML中实现多文件上传是通过用<input type"file">元素的multiple属性,以下简单描述多文件上传的步骤 HTML表单准备,使用<input type"file">元素,并为其添加multiple属性,以允许用户选择多个文件…...
2024.1.7力扣每日一题——赎金信
2024.1.7 题目来源我的题解方法一 哈希表方法二 数组 题目来源 力扣每日一题;题序:383 我的题解 方法一 哈希表 使用哈希表记录ransomNote中所需字符的数量,然后遍历magazine并将哈希表中存在的对应的数量减一 时间复杂度:O(nm…...
C#中List<T>底层原理剖析
C#中List底层原理剖析 1. 基础用法2. List的Capacity与Count:3.List的底层原理3.1. 构造3.2 Add()接口3.3 Remove()接口3.4 Inster()接口3.5 Clear()接口3.6 Contains()接口3.7 ToArray()接口3.8 Find()接口3.8 Sort()接口 4. 总结5. 参考 1. 基础用法 list.Max() …...
Leetcode 3003. Maximize the Number of Partitions After Operations
Leetcode 3003. Maximize the Number of Partitions After Operations 1. 解题思路2. 代码实现 题目链接:10038. Maximize the Number of Partitions After Operations 1. 解题思路 这一题我看实际比赛当中只有72个人做出来,把我吓得够呛,…...
MySQL第一讲:MySQL知识体系详解(P6精通)
MySQL知识体系详解(P6精通) MySQL不论在实践还是面试中,都是频率最高的。本系列主要对MySQL知识体系梳理,将给大家构建JVM核心知识点全局知识体系,本文是MySQL第一讲,MySQL知识体系详解。 文章目录 MySQL知识体系详解(P6精通)1、MySQL学习建议1.1、为什么学习 MySQL?1.2、…...
逻辑回归简单案例分析--鸢尾花数据集
文章目录 1. IRIS数据集介绍2. 具体步骤2.1 手动将数据转化为numpy矩阵2.1.1 从csv文件数据构建Numpy数据2.1.2 模型的搭建与训练2.1.3 分类器评估2.1.4 分类器的分类报告总结2.1.5 用交叉验证(Cross Validation)来验证分类器性能2.1.6 完整代码…...
Python print 高阶玩法
Python print 高阶玩法 当涉及到在Python中使用print函数时,有许多方式可以玩转文本样式、字体和颜色。在此将深入探讨这些主题,并介绍一些print函数的高级用法。 1. 基本的文本样式与颜色设置 使用ANSI转义码 ANSI转义码是一种用于在终端࿰…...
Wpf 使用 Prism 实战开发Day09
设置模块设计 1.效果图 一.系统设置模块,主要有个性化(用于更改主题颜色),系统设置,关于更多,3个功能点。 个性化的颜色内容样式,主要是从 Material Design Themes UI简称md、提供的demo里复制代码过来使用的。 1.设置…...
网络端口(包括TCP端口和UDP端口)的作用、定义、分类,以及在视频监控和流媒体通信中的定义
目 录 一、什么地方会用到网络端口? 二、端口的定义和作用 (一)TCP协议和UDP协议 (二)端口的定义 (三)在TCP/IP体系中,端口(TCP和UDP)的作用 (…...
flink如何写入es
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、写入到Elasticsearch5二、写入到Elasticsearch7总结 前言 Flink sink 流数据写入到es5和es7的简单示例。 一、写入到Elasticsearch5 pom maven依赖 <d…...
Java、Python、C++和C#的界面开发框架和工具的重新介绍
好的,以下是Java、Python、C和C#的界面开发框架和工具的重新介绍: Java界面开发: Swing: 是Java提供的一个基于组件的GUI工具包,可以创建跨平台的图形用户界面。它提供了丰富的组件和布局管理器,使得界面开发相对简单。…...
Java二叉树的遍历以及最大深度问题
Java学习面试指南:https://javaxiaobear.cn 1、树的相关概念 1、树的基本定义 树是我们计算机中非常重要的一种数据结构,同时使用树这种数据结构,可以描述现实生活中的很多事物,例如家谱、单位的组织架构、等等。 树是由n&#…...
Apollo 9.0搭建问题记录
虚拟机安装 可以看这个:https://blog.csdn.net/qq_45138078/article/details/129815408 写的很详细 内存 为了学习 Apollo ,所以只是使用了虚拟机,内存得大一点(128G),第一次,就是因为分配内…...
【心得】PHP文件包含高级利用攻击面个人笔记
目录 一、nginx日志文件包含 二、临时文件包含 三、php的session文件包含 四、pear文件包含 五 、远程文件包含 文件包含 include "/var/www/html/flag.php"; 一 文件名可控 $file$_GET[file]; include $file.".php"; //用php伪协议 ࿰…...
[scala] 列表常见用法
文章目录 不可变列表 List可变列表 ListBuffer 不可变列表 List 在 Scala 中,列表是一种不可变的数据结构,用于存储一系列元素。列表使用 List 类来表示,它提供了许多方法来操作和处理列表。 下面是一些常见的使用列表的示例: 创…...
python 使用urllib3发起post请求,携带json参数
当通过python脚本,发起http post请求,网络上大多是通过fields传递数据,然而这样,服务器收到的请求,但无法解析json数据。类似这些链接: Python urllib3库使用指南 软件测试|Python urllib3库使用指南 p…...
深入理解堆(Heap):一个强大的数据结构
. 个人主页:晓风飞 专栏:数据结构|Linux|C语言 路漫漫其修远兮,吾将上下而求索 文章目录 前言堆的实现基本操作结构体定义初始化堆(HeapInit)销毁堆(HeapDestroy) 重要函数交换函数(…...
抖音在线查权重系统源码,附带查询接口
抖音权重在线查询只需输入抖音主页链接,即可查询作品情况。 搭建教程 上传源码并解压 修改数据库“bygoukai.sql” 修改“config.php” 如需修改水印请修改第40行 如需修改限制次数,请修改第156行 访问域名user.php即可查看访问用户,停…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
