React16源码: React中创建更新的方式及ReactDOM.render的源码实现

React当中创建更新的主要方式

  • ReactDOM.render || hydrate
    • 这两个API都是我们要把整个应用第一次进行渲染到我们的页面上面
    • 能够展现出来我们整个应用的样子的一个过程
    • 这是初次渲染
  • setState
    • 后续更新应用
  • forceUpdate
    • 后续更新应用
  • replaceState
    • 在后续被舍弃

关于 ReactDOM.render


1 )概述

  • 它先要去创建一个 ReactRoot,这是一个包含react它整个应用的一个最顶点的一个对象
  • 之后是创建 FiberRootRootFiber
  • 第三步,就是要创建一个更新,创建更新之后,应用就可以进入到一个更新调度的阶段
    • 在进入调度之后,不管是通过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: DOMContainer
    • forceHydrate: boolean
      • 我们在函数调用栈向上溯源,传进去的 forceHydrate 是一个 false,这是一开始就写死的
      • 我们在最顶层对比,可知,在 626 行的 hydrate 方法的第四个参数传递的是 true
      • 因为hydraterender方法本质是一样的,唯一的一个区别,就是是否会调和原来存在于这个dom节点
      • 就是我们 container里面的它的HTML的节点, 是否要复用这些节点
      • 它主要是在有服务端渲染的情况下会使用 hydrate 这个API
      • 因为服务端渲染出来的情况,它里面的dom节点应该是跟客户端渲染的时候,第一次渲染,它得到的节点是一模一样的
      • 这个时候如果可以复用这些dom节点,可以提高一定的性能
      • 所以hydraterender内部的唯一的区别就是传的第四个参数是truefalse
  • 再回到 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

  • 再退回到调用 legacyCreateRootFromDOMContainerlegacyRenderSubtreeIntoContainer 函数中

    • 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.contextcontainer.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更新中,最复杂的逻辑

简单总结

  • ReactDOM.render 过程当中,创建了一个 ReactRoot
  • 同时在 ReactRoot 创建的过程中创建了 FiberRoot
  • FiberRoot 在创建的过程中也会自动去初始化一个 Fiber 对象(上面暂没有涉及)
  • 然后又在这个 root 上面去创建了一个 expirationTime
  • 之后又创建了一个 update 这个更新的对象,然后把这个更新的对象放到我们的 root 的节点上面
  • 之后就进入了一个更新的过程, 这就是一个创建更新的过程
  • 创建完更新, 再去实际的调度整个任务的更新

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

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

相关文章

Qt undefined reference to `vtable for xxx‘

一、问题背景 在编译QT代码时&#xff0c;出现 undefined reference to xxx::entered()&#xff0c;通过鼠标双击QtCreator“问题栏”中的该行&#xff0c;则会跳转到发送信号的代码所在行。与上述代码一同出现在“问题栏”的还有 undefined reference to vtable for xxx’。 …

Git常用命令diff和mv

Git常用命令diff和mv 1、diff # 查看工作区和暂存区所有文件的对比 # 该命令可以显示尚未添加到stage的文件的变更 $ git diff# 查看工作区和暂存区单个文件的对比 $ git diff file# 显示暂存区和上一个commit的差异 # 查看暂存区与指定提交版本的不同,版本可缺省为HEAD $ gi…

力扣(leetcode)第412题Fizz Buzz(Python)

412.Fizz Buzz 题目链接&#xff1a;412.Fizz Buzz 给你一个整数 n &#xff0c;找出从 1 到 n 各个整数的 Fizz Buzz 表示&#xff0c;并用字符串数组 answer&#xff08;下标从 1 开始&#xff09;返回结果&#xff0c;其中&#xff1a; answer[i] “FizzBuzz” 如果 i 同…

Linux-文件系统管理实验2

1、将bin目录下的所有文件列表放到bin.txt文档中&#xff0c;并将一共有多少个命令的结果信息保存到该文件的最后一行。统计出文件中以b开头的所有命令有多少个&#xff0c;并将这些命令保存到b.txt文档中。将文档中以p结尾的所有命令保存到p.txt文件中&#xff0c;并统计有多少…

lv14 ioctl、printk及多个此设备支持 6

1 ioctl操作实现 对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; long xxx_ioctl (struct file *filp, unsigned int cmd, unsigned long arg); 功能&#xff1a;对相应设备做指定的控制操作&#xff08;各种属性的设置获取等等&#xff09; 参数…

【csharp】依赖注入

依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;DI&#xff09;是一种软件设计模式&#xff0c;旨在降低组件之间的耦合度。在依赖注入中&#xff0c;一个类的依赖关系不是在类内部创建&#xff0c;而是通过外部传递进来。这通常通过构造函数、方法参数或属性…

氢燃料电池技术综述

文章目录 工作原理 系统集成 应用 特点 国家政策 行业发展 机遇和挑战 参考文献 工作原理 氢燃料电池是通过催化剂将氢气和氧气反应生成电能和水的过程&#xff0c;在这个过程中会伴随有热量产生。 系统集成 氢燃料电池需要将氢气供应系统、氧气供应系统、电堆、冷却系…

【基础篇】十二、引用计数法 可达性分析算法

文章目录 1、Garbage Collection2、方法区的回收3、堆对象回收4、引用计数法5、可达性分析算法6、查看GC Root对象 1、Garbage Collection C/C&#xff0c;无自动回收机制&#xff0c;对象不用时需要手动释放&#xff0c;否则积累导致内存泄漏&#xff1a; Java、C#、Python、…

Linux程序、进程以及计划任务(第一部分)

目录 一、程序和进程 1、什么是程序&#xff1f; 2、什么是进程&#xff1f; 3、线程是什么&#xff1f; 4、如何查看是多线程还是单线程 5、进程结束的两种情况&#xff1a; 6、进程的状态 二、查看进程信息的相关命令 1、ps&#xff1a;查看静态的进程统计信息 2、…

c++基础(对c的扩展)

文章目录 命令空间引用基本本质引用作为参数引用的使用场景 内联函数引出基本概念 函数补充默认参数函数重载c中函数重载定义条件函数重载的原理 命令空间 定义 namespace是单独的作用域 两者不会相互干涉 namespace 名字 { //变量 函数 等等 }eg namespace nameA {int num;v…

判断对象是否是垃圾的引用计数法有什么问题

给对象中添加一个引用计数器&#xff0c;每当有一个地方引用它&#xff0c;计数器就加一&#xff0c;当引用失效计数器就减一&#xff0c;任何时候引用计数器为0的对象就是不可能再被使用的&#xff08;变成垃圾&#xff09;。 这个方法实现简单、效率高&#xff0c;但是目前主…

STM32F407ZGT6-flash地址-SRAM

2、 2-STM32F407英文数据手册&#xff08;没有中文&#xff09;.pdf Memory mapping

抽丝剥茧设计模式

Singleton 单例 饿汉式 最简单的方式 /*** 饿汉式* 类加载到内存后&#xff0c;就实例化一个单例&#xff0c;JVM保证线程安全* 简单实用&#xff0c;推荐使用&#xff01;* 唯一缺点&#xff1a;不管用到与否&#xff0c;类装载时就完成实例化* Class.forName(""…

Leetcode 2998. Minimum Number of Operations to Make X and Y Equal

Leetcode 2998. Minimum Number of Operations to Make X and Y Equal 1. 解题思路2. 代码实现 题目链接&#xff1a;10033. Minimum Number of Operations to Make X and Y Equal 1. 解题思路 这一题就是一个比较简单的动态规划的题目了。 显然&#xff0c;如果x小于y&…

Kafka 基本概念和术语

1、消息 Record&#xff1a;Kafka 是消息引擎嘛&#xff0c;这里的消息就是指 Kafka 处理的主要对象。 2、主题 Topic&#xff1a;主题是承载消息的逻辑容器&#xff0c;在实际使用中多用来区分具体的业务。在Kafka 中发布订阅的对象是 Topic。 3、分区 Partition&#xf…

原子性、CAS操作

Java中的原子性操作 所谓原子性操作&#xff0c;是指执行一系列操作时&#xff0c;这些操作要么全部执行&#xff0c;要么全部不执行&#xff0c;不存在只执行其中一部分的情况。 在设计计数器时一般都先读取当前值&#xff0c;然后1,再更新。 这个过程是读—改—写的过程&a…

python归并排序

归并排序&#xff08;Merge Sort&#xff09;是一种经典的排序算法&#xff0c;它采用分治法的一个非常典型的应用。该算法将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&#xff0c;再使子序列段间有序。其基本步骤如下&#xff1a; 1…

msckf-vio 跑Euroc数据集,并用evo进行评估

所需材料&#xff1a; Euroc数据集主页&#xff1a;https://projects.asl.ethz.ch/datasets/doku.php?idkmavvisualinertialdatasetsevo评估工具代码&#xff1a;https://github.com/MichaelGrupp/evo向msckf-vio中添加保存位姿的代码&#xff0c;可参考https://blog.csdn.ne…

C++中使用vector保存新建对象中自指指针的问题

问题 在某些场景中&#xff08;例如并查集&#xff09;&#xff0c;我们需要将新建对象中的指针指向对象自己。例如&#xff0c; struct factor {int data;factor* next;factor(int i) : data(i), next(this){} }; 这样的结构体当然没有问题&#xff0c;如果我们想以类似链表…