React源码 ReactDOM.render

在 react 当中,主要创建更新的有三种方式

1、ReactDOM.render || hydrate

这两个api都是要把这个应用第一次渲染到我们页面上面,展现出来整个应用的样子的过程,这就是初始渲染

2、setState

3、forceUpdate

ReactDOM.render 需要做哪些事情呢?

1、创建 ReactRoot

2、创建 FiberRoot 和 RootFiber

3、创建更新

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: ‘jokcy‘,
  }

  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

这个 demo 非常简单,一个 Input  一个 List ,Input 里面有个 state ,属性有 name,然后 render 里面有个 input 标签,这个标签,输入的时候去改变这个 name 。

第二个组件是 List ,这个 List 里面有三个 span ,三个 span 里面分别显示 state 的属性 a,b,c ,然后点击 click ,a,b,c 以乘以自己的方式改变值。这就是这个 demo 的样子。 然后我们看一下最终要渲染这个应用,会调用 ReactDOM.render ,然后传入这个 App ,第二个是这个应用会挂载到的节点上

import React from ‘react‘
import ReactDOM from ‘react-dom‘
import ‘./index.css‘
import App from ‘./App‘;

ReactDOM.render(<App />, document.getElementById(‘root‘))

我们这里传入的是 <App /> ,实际上我们传入的是 React.createElement。我们传进去的是 App 这个类,但是并没有创建这个实例,这个时候还什么东西都没有,因为我们只得到了一个 createElement,最终要形成一个页面渲染出来的过程,这就是 ReactDOM.render 要做的事情,然后看下这个源码,打开 ReactDOM.js。 react-dom 下面有不同的包,有 client , 有 server . 还有 shared , shared 是在 cilent 和 server 里面都会用到的。对应的就是渲染的平台不一样,server 就是在 Nodejs 下面进行渲染的一个工具包,我们主要了解的是 client 下面的东西。在 ReactDOM.js 下面,我们先找到定义 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,
    ],
  },
};

这段就是定义 ReactDOM 的地方。ReactDOM 是个对象,这个对象里面有 render 方法。这个 render 方法我们可以看到他接收的有三个参数。一个是 element ,就是 react element . 第二个是 container ,就是我们要挂载到哪一个节点上面。第三个是 callback ,callback 就是说这个应用渲染结束之后,他会调用这个 callback 。

然后里面直接调用了一个方法叫 legacyRenderSubtreeIntoContainer,然后传入了 null ,element,container,false,callback 这几个参数。

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);
}

这个就是 legacyRenderSubtreeIntoContainer 这个方法,第一个参数 null ,他对应的是 parentComponent 这个参数

let root: Root = (container._reactRootContainer: any);

然后下面他获取了 root 这么一个变量,他通过 container , container 就是 DOMContainer ,就是 ReactDOM.render 里面的第二个参数,就是挂载的节点,它上面有没有 _reactRootContainer 这个属性,第一次渲染的时候,这个属性值肯定是没有的,接下来没有的话,他就会去创建这个属性值,通过调用 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);
}

我们看到这个方法,他接收两个参数,第一个是 DOMContainer , 第二个是 forceHydrate ,这个 forceHydrate 就是传入的第四个参数,也就是 render 方法里面的第四个参数,是 false , render 方法里面是写死的,是不是代表着永远都是 false 呢,这个主要对应的是另外一种情况,就是 hydrate 这个方法,hydrate 跟 render 本质是一样的,唯一的一个区别就是是否会调和 container 里面的 html 节点,是否要复用这些节点。主要是有服务端渲染的时候会使用 hydrate 这个 api。因为服务端渲染 和 客户端渲染 第一次得到的节点是一样的。这个时候如果可以复用这些节点,可以提高一定的性能。所以他两唯一的区别就是第四个参数是 true 还是 false 。

我们再回到 legacyCreateRootFromDOMContainer ,对于 render 来说,这个 forceHydrate 是 false。那么如果是 false 的情况,会去调用这个方法 shouldHydrateDueToLegacyHeuristic。

function shouldHydrateDueToLegacyHeuristic(container) {
  const rootElement = getReactRootElementInContainer(container);
  return !!(
    rootElement &&
    rootElement.nodeType === ELEMENT_NODE &&
    rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
  );
}

这个方法里面 return 的是 rootElement 。rootElement 是通过调用 getReactRootElementInContainer 得到的

function getReactRootElementInContainer(container: any) {
  if (!container) {
    return null;
  }

  if (container.nodeType === DOCUMENT_NODE) {
    return container.documentElement;
  } else {
    return container.firstChild;
  }
}

这个方法先判断一下这个 container 是否存在,如果不存在, return,如果存在,再继续判断这个节点是否等于 DOCUMENT_NODE ,就是 document ,就是 window.document ,是否是等于的。如果是等于,就直接返回这个 document 。 如果不是等于,就返回这个 container 的子节点。再回到 shouldHydrateDueToLegacyHeuristic ,判断完他是否是个节点,而且是 ROOT_ATTRIBUTE_NAME,然后返回,这个不是特点重要,跟 react 整体更新没有多大关系,再回到 legacyCreateRootFromDOMContainer 。当这个节点是 false 的情况下,执行了一个 for 循环,这个 for 循环是干嘛的呢?就是把 container 传进来的所有子节点都删掉,因为我们认为 shouldHydrate 为 false 的情况下,在渲染出来后,这些子节点已经不能用了,因为不需要合并他。最后return 一个 new ReactRoot。他传进去的是 container, isConcurrent, shouldHydrate 。 isConcurrent 我们看到直接是 false,已经注明了 root 节点不应该是 async 的。接下来我们看一下 new 一个 root 的过程

function ReactRoot(
  container: Container,
  isConcurrent: boolean,
  hydrate: boolean,
) {
  const root = DOMRenderer.createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}
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;
};
ReactRoot.prototype.unmount = function(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(null, root, null, work._onCommit);
  return work;
};
ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function(
  parentComponent: ?React$Component<any, any>,
  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, parentComponent, work._onCommit);
  return work;
};
ReactRoot.prototype.createBatch = function(): Batch {
  const batch = new ReactBatch(this);
  const expirationTime = batch._expirationTime;

  const internalRoot = this._internalRoot;
  const firstBatch = internalRoot.firstBatch;
  if (firstBatch === null) {
    internalRoot.firstBatch = batch;
    batch._next = null;
  } else {
    // Insert sorted by expiration time then insertion order
    let insertAfter = null;
    let insertBefore = firstBatch;
    while (
      insertBefore !== null &&
      insertBefore._expirationTime <= expirationTime
    ) {
      insertAfter = insertBefore;
      insertBefore = insertBefore._next;
    }
    batch._next = insertBefore;
    if (insertAfter !== null) {
      insertAfter._next = batch;
    }
  }

  return batch;
};

ReactRoot 方法里面通过 DOMRenderer.createContainer(container, isConcurrent, hydrate) 创建了 root 节点。那么 DOMRenderer 是什么东西呢?

import * as DOMRenderer from ‘react-reconciler/inline.dom‘;

DOMRenderer 其实就是 react-reconciler 下面的 inline.dom 。 inline.dom 里面只引用了 ReactFiberReconciler.js 。打开这个js 之后,找到 createContainer。

export function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}

发现这个方法里面创建了一个 createFiberRoot 。返回到 ReactRoot ,创建完之赋值给 this._internalRoot。然后再赋值给了 container._reactRootContainer ,最后,调用到了 root.render,就是原型链上的 render 方法。这里创建了一个 reactWork,这个不是特别重要,就跳过了。最终调用了 DOMRenderer.updateContainer。

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,
  );
}

updateContainer 他接收了 element,container,parentComponent。这个方法里面有三个变量。一个 current, 一个 currentTime。一个 expirationTime 。 expirationTime 是通过computeExpirationForFiber 获得的, expirationTime是非常重要的。然后调用 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 是没有的,最后调用了 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;
}

这里面创建了 createUpdate。update 是用来标记,react 中需要标记更新的地点的,这个方法就是设置了一些update 相关的属性。最后调用 enqueueUpdate 。后面调用了 scheduleWork ,调度,为什么有调度呢,因为有任务优先级的概念。

原文地址:https://www.cnblogs.com/wzndkj/p/11966100.html

时间: 2024-11-09 08:04:53

React源码 ReactDOM.render的相关文章

【React源码分析】组件通信、refs、key和ReactDOM

React源码系列文章,请多支持:React源码分析1 - 组件和对象的创建(createClass,createElement)React源码分析2 - React组件插入DOM流程React源码分析3 - React生命周期详解React源码分析4 - setState机制React源码分析5 -- 组件通信,refs,key,ReactDOMReact源码分析6 - React合成事件系统 1 组件间通信 父组件向子组件通信 React规定了明确的单向数据流,利用props将数据从父组件传

React 源码剖析系列 - 生命周期的管理艺术

目前,前端领域中 React 势头正盛,很少能够深入剖析内部实现机制和原理. 本系列文章 希望通过剖析 React 源码,理解其内部的实现原理,知其然更要知其所以然. 对于 React,其组件生命周期(Component Lifecycle)是它的核心概念,本文从源码入手,来剖析 React 生命周期的管理艺术. 阅读本文需要对 React 有一定的了解,如果你不知何为组件的生命周期,请详读 React 生命周期的文档. 如果你对 React 组件的生命周期存在些许疑惑,如生命周期如何顺序管理:

React躬行记(16)——React源码分析

React可大致分为三部分:Core.Reconciler和Renderer,在阅读源码之前,首先需要搭建测试环境,为了方便起见,本文直接采用了网友搭建好的环境,React版本是16.8.6,与最新版本很接近. 一.目录结构 React采用了由Lerna维护monorepo方式进行代码管理,即用一个仓库管理多个模块(module)或包(package).在React仓库的根目录中,包含三个目录: (1)fixtures,给源码贡献者准备的测试用例. (2)packages,React库提供的包的

读react源码准备

git源码地址:https://github.com/facebook/react react 里面就是 react源码 react里面的react文件夹就是react源码,react源码非常的少,总共不到100行,ReactBaseClasses也就一百多行,react和react-dom加起来有一百多k,因为大部分源码是在react-dom里面,所以React本身是个定义,就是定义节点,以及他的表现行为的一个包,具体我们在dom上面如何去渲染,如何去更新,这些操作都是跟平台相关的.在reac

React 源码深度解析 高级前端工程师必备技能完整教程百度云

原文配套资源获取链接:点击获取 第1章 课程导学 对课程整体进行讲解. 1-1 课程导学 第2章 基础知识 React API 一览 React主要API介绍,在这里你能了解它的用法,为下一章源码分析打基础. 2-1 准备工作 2-2 JSX到JavaScript的转换 2-3 react-element 2-4 react-component 2-5 react-ref 2-6 forward-ref 2-7 context 2-8 concurrent-mode 2-9 suspense-a

React源码

预热 redux 函数内部包含了大量柯里化函数以及代码组合思想 柯里化函数(curry) 通俗的来讲,可以用一句话概括柯里化函数:返回函数的函数 // example const funcA = (a) => { return const funcB = (b) => { return a + b } }; 上述的funcA函数接收一个参数,并返回同样接收一个参数的funcB函数. 柯里化函数有什么好处呢? 避免了给一个函数传入大量的参数--我们可以通过柯里化来构建类似上例的函数嵌套,将参数的

【React源码学习】Suspense &amp;&amp; Children

Suspense 16.6 提供的一个feature,在线源码地址:https://github.com/facebook/react/blob/master/packages/react/src/React.js 在一个Suspense组件,它下面渲染了一个或者多个异步的组件,有任何一个组件throw了一个promise之后,在这个promise的resolved之前,都会显示fallback中的内容. 也就是说,在一个Suspense组件中,有多个组件,它要等它里面所有组件都resolved

React源码 React.Children

children是什么意思呢?就是我们拿到组件内部的props的时候,有props.children这么一个属性,大部分情况下,我们直接把 props.children 渲染到 JSX 里面就可以了.很少有情况我们需要去操作这个 children . 但是如果一旦你需要去操作这个 children .我们推荐使用 React.children 的 api , 而不是直接去操作他. 虽然说我们大部分情况下拿到的 children 是合理的 react element 或者是一个数组,但是 Reac

React源码解析——创建更新过程

一.ReactDOM.render 创建ReactRoot,并且根据情况调用root.legacy_renderSubtreeIntoContainer或者root.render,前者是遗留的 API 将来应该会删除,根据ReactDOM.render的调用情况也可以发现parentComponent是写死的null DOMRenderer.unbatchedUpdates制定不使用batchedUpdates,因为这是初次渲染,需要尽快完成. 原文地址:https://www.cnblogs.