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 文件,找到
createContainer
export 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即可查看访问用户,停…...
Spring Framework和SpringBoot的区别
目录 一、前言 二、什么是Spring 三、什么是Spring Framework 四、什么是SpringBoot 五、使用Spring Framework构建工程 六、使用SpringBoot构建工程 七、总结 一、前言 作为Java程序员,我们都听说过Spring,也都使用过Spring的相关产品࿰…...
2024--Django平台开发-Django知识点(三)
day03 django知识点 项目相关路由相关 urls.py视图相关 views.py模版相关 templates资源相关 static/media 1.项目相关 新项目 开发时,可能遇到使用其他的版本。虚拟环境 老项目 打开项目虚拟环境 1.1 关于新项目 1.系统解释器命令行【学习】 C:/python38- p…...
Github 2024-01-08开源项目周报 Top14
根据Github Trendings的统计,本周(2024-01-08统计)共有14个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目3C项目2Dart项目1QML项目1Go项目1Shell项目1Rust项目1JavaScript项目1C#项目1 免费…...
vue3 的内置组件汇总
官方给出的说明: Fragment: Vue 3 组件不再要求有一个唯一的根节点,清除了很多无用的占位 div。Teleport: 允许组件渲染在别的元素内,主要开发弹窗组件的时候特别有用。Suspense: 异步组件,更方便开发有异步请求的组件。 一、fr…...
ARM工控机Node-red使用教程
嵌入式ARM工控机Node-red安装教程 从前车马很慢书信很远,而现在人们不停探索“科技改变生活”。 智能终端的出现改变了我们的生活方式,钡铼技术嵌入式工控机协助您灵活布建能源管理、大楼自动化、工业自动化、电动车充电站等各种多元性IoT应用ÿ…...
Visual Studio 发布程序自动更新 ClickOnce和AutoUpdater测试
文章目录 前言运行环境ClickOnce(Visual Studio 程序发布)IIS新建文件夹C# 控制台测试安装测试更新测试卸载 AutoUpdaterDotNET实现原理简单使用新建一个WPF项目 代码封装自动更新代码封装简单使用 总结 前言 虽然写的大部分都是不联网项目,…...
Codeforces Round 761 (Div. 2) E. Christmas Chocolates(思维题 树的直径 二进制性质 lca)
题目 n(n<2e5)个值,第i个值ai(0<ai<1e9),所有ai两两不同 初始时,选择两个位置x,y(x≠y),代表需要对这两个位置进行操作,要把其中一个值变成另一个 你可以执行若干次操作,每一次,你可…...
知识图谱之汽车实战案例综述与前瞻分析
知识图谱的前置介绍 什么是知识图谱 知识图谱本质(Knowledge Graph)上是一种叫做语义网络(semantic network ) 的知识库,即具有有向图结构的一个知识库;图的结点代表实体(entity)或者概念(con…...
网关Gateway
什么是网关? 网关实质上是一个网络通向其他网络的 IP 地址,是当前微服务项目的"统一入口"。 网关能做什么? 反向代理 、鉴权、 流量控制、 熔断、 日志监控等 图片原文:http://t.csdnimg.cn/SvUJh 核心概念 Router(…...
java 生成一个当前时间的时间搓
开发过程中 用时间搓数值格式存储 会更加精准 那么 我们在一些日常增删查改中就可以用时间搓来记录操作时间 就一行代码 long timestamp System.currentTimeMillis();他就能生成当前时间的时间搓 运行结果如下 然后 我们可以在 http://shijianchuo.wiicha.com/ 上进行转换查…...
有没有做旅游攻略的网站/竞价排名营销
实时人群计算——设想 目的 做到实时人群打标技术 spark streaming在前段创建一些判断条件,例如订单访问次数等,以此为依据创建人群。spark streaming 周期从mysql拉取新创建的人群以batch进来的人为key,从kv存储中拉取当前人的信息把获取的数…...
做不了大流量网站/seo咨询河北
sql server 2008如何导入mdf,ldf文件网上找了很多解决sql server导入其他电脑拷过来的mdf文件,多数是不全,遇到的解决方法不一样等问题,下边是找到的解决问题的最全面方法!将mdf, ldf文件导入到sql server …...
长春市快速建站网站/站长工具名称查网站
说到BYOD,可以说是这几年的一个热点了,要说在企业网WLAN市场什么最热,当属BYOD,君不见无线的老牌劲旅思科、aruba早都祭出了BYOD的大旗,而原本盘踞在运营商的华为也推出了BYOD解决方案,试图借此机会大举进入…...
如何建立营销网络/seo sem优化
1、下载和安装maven工具 https://maven.apache.org/download.cgi 下载解压,修改conf下的setting.xml,优先从阿里云镜像拉取关联包 <mirrors><!-- mirror| Specifies a repository mirror site to use instead of a given repository. The repository that| this mi…...
四川省政府网站集约化建设文件/泰安网站建设
简介: RocketMQ作为一款纯java、分布式、队列模型的开源消息中间件,支持事务消息、顺序消息、批量消息、定时消息、消息回溯等。 发展历程: 1. Metaq(Metamorphosis) 1.x 由开源社区killme2008维护,开源社区非常活跃。https://git…...
乐拾seo/新网seo关键词优化教程
/*** JS AOP方法拦截方法,只要返回false,后边的方法就不再执行* 调用示例 :* func func.before(beforefn).after(afterfn);* func();*/ Function.prototype.before function(beforefn){var _self this;return function(){if(beforefn…...