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

centos7搭建LNMP环境-编译安装&yum安装-超详细 文章目录1. 首先了解编译安装和yum安装的区别。2. yum安装准备工作---切换阿里云yum源3. 编译安装lnmp4.yum安装lnmp5. lnmp一键安装6. 一些报错7. 笔记8. 参考文章 1. 首先了解编译安装和yum安装的区别。 windows下安装centos虚拟机,之前写过。传送门~~ 编译安装: 可以按需安装,想安在哪个目录下就安在哪个目录下。 可以设定参数 可安装自己想要的版本 yum安装: 安装的方便快捷 不需要考虑包的依赖 可以直接使用service nginx st 阅读详情

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 } = oldState
      return {
        a: a * a,
        b: b * b,
        c: c * c,
      }
    })
  }

  render() {
    const { a, b, c } = this.state
    return [
      <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 (
      <input
        type="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 check
      invariant(
        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 mount
        root = 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);
          };
        }
        // Update
        if (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
      • 因为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更新中,最复杂的逻辑

简单总结

  • 在 ReactDOM.render 过程当中,创建了一个 ReactRoot
  • 同时在 ReactRoot 创建的过程中创建了 FiberRoot
  • FiberRoot 在创建的过程中也会自动去初始化一个 Fiber 对象(上面暂没有涉及)
  • 然后又在这个 root 上面去创建了一个 expirationTime
  • 之后又创建了一个 update 这个更新的对象,然后把这个更新的对象放到我们的 root 的节点上面
  • 之后就进入了一个更新的过程, 这就是一个创建更新的过程
  • 创建完更新, 再去实际的调度整个任务的更新
图像处理之边缘检测[微分算子、Canny算子和LOG算子] LOG算子是在经典算子的基础上发展起来的边缘检测算子,根据信噪比求得检测边缘的最优滤波器。边缘总是以强度突变的形式出现,可以定义为图像局部特性的不连续性,如灰度的突变和纹理结构的突变等。图像的边缘包含了物体形状的重要信息,它不仅在分析图像时大幅度地减少了要处理的信息量,而且还保护了目标的边界结构。对于复杂的图像,采用Roberts算子不能较好地得到图像的边缘,而需要采用更加复杂的3X3的算子比如Prewitt算子。对于离散的图像f(x,y),边缘检测算子是用图像的垂直和水平差分来逼近梯度算子,即。 阅读详情

相关推荐

Simulink机电仿真避坑指南:如何用MATLAB解决液压系统建模的3大典型问题

本文针对液压系统建模在Simulink动态仿真中常见的三大难题——参数物理意义缺失、模型收敛性差及性能优化瓶颈,提供了专业的MATLAB解决方案。文章详细阐述了如何从物理参数推导仿真模型、破解代数环与刚性系统问题,并分享了模型降阶与加速技巧,旨在帮助工程师构建精确高效的机电系统仿真模型。

o9p0q1的博客 295

React16入门

环境搭建 安装node 安装官方脚手架create-react-app npm install -g create-react-app 创建项目 进入文件夹,进入cmd运行命令 mkdir ReactDemo //创建ReactDemo文件夹 create-react-appdemo //用脚手架创建React项目 cd demo //等创建完成后,进入项目目录 npm start //预览项目,如果能正常打开,说明项目创建成功 脚手架生成目录介绍 README.md 这个文件主要作用就是对项目的说

Ruebass的博客 1298

5分钟搞定!用ZLMediaKit搭建GB28181视频监控系统(含避坑指南)

本文提供了一份基于ZLMediaKit搭建GB28181视频监控系统的实战指南。文章详细介绍了从环境准备、核心组件(ZLMediaKit与SIP服务器)部署、摄像头配置到内外网穿透的完整流程,并重点分享了设备ID配置、防火墙设置等关键避坑经验,帮助开发者快速构建稳定可控的监控平台。

apple5的专栏 1088

react学习 新建react项目

一.搭建本地开发环境

似水流年,举重若轻 2664

MT5策略如何设置回测结果才能最接近实盘

摘要:在MT5回测中,选择"每笔分笔报价"模式最接近实盘交易,因其使用真实Tick数据模拟价格波动、点差和滑点。关键设置包括:下载高质量历史数据、合理设定点差和滑点、使用正常回测速度。该模式能准确测试挂单、止盈止损等策略,虽然速度较慢但结果最可靠,避免其他模式造成的失真,是评估交易策略表现的最佳选择。(149字)

weixin_38915451的博客 1879

海思Hi3519AV100 sensor移植实战:IMX586驱动调试与硬件验证全流程

本文详细解析了在海思Hi3519AV100平台上移植索尼IMX586传感器的全流程实战。内容涵盖从硬件I2C通信验证、驱动代码移植与修改,到内核配置、时钟设定,以及使用示波器进行硬件信号深度验证和ISP图像调优等关键步骤,为开发者提供了一套清晰、可复现的sensor移植与调试方法。

weixin_29168393的博客 395

React v16源码之ReactDOM.render

React提供render API来实现将组件构成的视图渲染挂载到指定的DOM节点上,本文旨在梳理ReactDOM.render函数的执行逻辑,从而进一步理解React 16版本之后的Fiber以及相关处理过程(React版本是16.14.0)。

玉案轩窗的博客 1527

RH850从0搭建Autosar开发环境系列讲解 - 总目录

Davinci Autosar实战开发从0构建开发能力。OS,COM,DCM,BSWMM,ECUM,VLINK,XCP,MEMMAP等模块配置,带你熟悉运行的autosar代码,包括MICROSAR的启动运行,使用GreenHills生成运行的hex。

weixin_42412049的博客 1万+

学习React系列(六)——更新dom细节于原理

React更新dom的依据: 1、不同类型的elements会产生不同的树 2、通过render方法中包含key属性的子元素,开发者可以示意哪些子元素可能是稳定的。 更新过程: 一、根元素类型不同:旧树被卸载,旧的Dom节点销毁,Component实例将接收componentWillUnmount()方法, 当新建一个树,新节点插入到Dom中,Component实例接收compon...

weixin_34418883的博客 473

倍福--实现TCP/IP的配置

控制器为CX5120,需要和上位机做通信,选择TCP/IP的通讯方式,本文介绍TCP/IP通信的操作步骤。 目 录 软硬件版本 3 1.1. 倍福Beckhoff 3 1.1.1. 控制器硬件 3 1.1.2. 控制软件 3 1.2. 串口调试助手 3 1.2.1. TCP调试通信助手 3 授权文件 3 2.1 Lib文件 3 2.2. CAB文件 4 PLC程序编写 4 3.1. 引入TCP/IP库文件 4 3.2. Client端程序 5 3.2.1. 建立相关变量 5 3.3.2. 编写连接功能块

weixin_41883890的博客 5730

YOLOv8/v9 遥感图像目标检测实战指南:建筑物检测与变化监测

遥感图像在城市规划、灾害评估、农业监管等领域应用广泛,但传统目标检测方法存在效率低、精度不足、难以适配遥感图像大尺寸、多尺度目标等问题。本文以“建筑物检测与变化监测”为核心,基于DOTA、NWPU VHR-10等公开数据集,系统讲解YOLOv8/v9在遥感图像中的实战流程。从数据获取、格式转换、模型训练优化,到大图切片推理、建筑物变化监测,全程提供可复现的代码与参数配置。通过针对性优化(旋转增强、切片推理等),模型在NWPU VHR-10数据集上mAP50达0.85以上,可适配卫星影像、无人机航拍等多场景

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 520

React16、17和Vue2、3生命周期、渲染顺序

主要就是命名不同。

luemeon的博客 1745

TGRS 2026 即插即用 | 注意力篇 | SCFM:双向交叉显著性引导注意力,高层语义与低层细节的双向互补!

本文介绍了MPCNet中的语义引导交叉注意力融合模块(SCFM),该模块通过双向交叉显著性引导机制实现跨尺度特征融合。SCFM创新性地结合了高层语义和低层细节的互补信息,通过轻量局部空间注意力预增强和坐标注意力全局融合引导,显著提升了红外小目标检测的精度。实验表明,SCFM在多个数据集上达到SOTA性能,同时保持轻量高效的计算特性,支持端侧设备部署。 SCFM的核心贡献包括: 双向交叉显著性引导机制,实现语义与细节的互补融合 轻量局部空间注意力预增强模块,计算量仅为传统方法的1/10 坐标注意力全局融合引导

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 151

React16源码解读:揭秘ReactDOM

经过以上分析,现在我们就可以很容易地区分出客户端渲染和服务端渲染,并且在面试中如果被问到两种渲染模式的区别,我们就可以很轻松地在源码级别上说出两者的实现差异,让面试官眼前一亮。这两个概念在 React 的整个任务调度过程中起着关键性的作用,如果不理解这两个概念,后续的任务调度过程就是空谈,所以这里也是我们必须要去理解的部分。@param callback ReactDOM.render()或者ReactDOM.hydrate()中的第三个参数,组件渲染完成后需要执行的回调函数。

2401_84007049的博客 1013

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

React源码解析笔记---ReactDOM.render

import ReactDOM from 'react-dom'; ReactDOM.render(<App />, document.getElementById('root)); 源码: 这里只摘录了核心代码 const ReactDOM: Object = { ... render( element: React$Element<any>, // ...

ck_1的博客 811

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

React16源码: React中调度之scheduleWork的源码实现

定位到 packages/react-reconciler/src/ReactFiberScheduler.js。拿之前的一个 Fiber Tree 节点来举例。当中,它需要去找到更新对应的。如果符合条件需要重置 stack。如果符合条件就去请求工作调度。这几个方法最终都调用了。

Wang的专栏 1525

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

React16源码解读:揭秘ReactDOM,前端开发移动

然后我们将代码定位到第632行,可以看到ReactDOM对象包含了很多我们可能使用过的方法,例如renderhydrate和等。本文中我们暂且只关心render方法,但为了方便对比,也可以简单看下hydrate方法:/**服务端渲染@param element 表示一个ReactNode,可以是一个ReactElement对象@param container 需要将组件挂载到页面中的DOM容器@param callback 渲染完成后需要执行的回调函数*/invariant(

2401_84094725的博客 884

React源码解读之更新的创建

React的鲜活生命起源于,这个过程会为它的一生储备好很多必需品,我们顺着这个线索,一探婴儿般React应用诞生之初的悦然。更新创建的操作我们总结为以下两种场景。

weixin_59558923的博客 599

React16源码: React中的update和updateQueue的源码实现

在 `ReactDOM.render` 过程中,还需要创建一个 update 对象, `update` 用于记录组件状态的改变的一个对象,它存放于Fiber对象的 `updateQueue` 中,`updateQueue`,它是一个单向链表的结构,一次整体的更新过程当中, 可能在这个queue里会存在多 Update

Wang的专栏 1665

React16源码: React中FiberRoot的源码实现

在 `ReactDOM.render` 过程当中,创建了一个 `ReactRoot`的对象, 这个 `ReactRoot` 对象最主要承担了创建 `FiberRoot` 对象, 这个对象它非常重要,在后期整个应用调度过程当中都会跟它有关

Wang的专栏 1413

React16源码: React中的updateHostRoot的源码实现

`HostRoot` 是一个比较特殊的节点, 因为在一个react应用当中,它只会有一个 `HostRoot`, 它对应的 Fiber 对象是我们的 `RootFiber` 对象,重点在于它的更新过程

Wang的专栏 1392
上一篇: Go语言中的init函数的执行时机
下一篇: React16源码: React中FiberRoot的源码实现
Wang's Blog
博客等级 码龄12年 7433粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值