react 源码解析 2-1 react-api

目录解析

  1. packages/events 事件系统
  2. packages/react
  3. packages/react-dom
  4. packages/react-reconciler
  5. packages/scheduler

jsx 到 javascript转换过程

看下babel编译结果javascript

首字母是不是大小写 判断翻译成字符串仍是变量组件的原理 html

createElement

源码文件目录 java

createElement有三个参数 type, config, children。 点击查看api 文档
实现的主要思路是: 点击进入下文代码 demo 在线编辑地址react

  1. 提取props,将key,ref,__self,__source 赋值
if (config != null) {
    // 剔除config中的 key ref self source 属性 ,并赋予值
    if (hasValidRef(config)) {
      ref = config.ref;
    }
    if (hasValidKey(config)) {
      key = "" + config.key;
    }

    self = config._self === undefined ? null : config._self;
    source = config.__source === undefined ? null : config.__source;

    // 将properties 添加到新的props对象中
    // 使用hasOwnProperty.call 为防止 config对象自定义hasOwnProperty 属性 
    // 具体解释点击 https://www.jianshu.com/p/95839681776d
    // 未知对象用 hasOwnProperty.call 已知对象能够直接使用 hasOwnProperty
    for(propName in config){
      if(
        hasOwnProperty.call(config,propName) && 
        !RESERVED_PROPS.hasOwnProperty(propName)
      ){
        props[propName] = config[propName];
      }
    }
  }
复制代码
  1. Children can be more than one argument, and those are transferred onto the newly allocated props object
// children 处理获得 props.children
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
      props.children = children;
    } else if (childrenLength > 1) {
      const childrenArray = Array(childrenLength);
      for (let i = 0; i < childrenLength; i++) {
        childrenArray[i] = arguments[i + 2];
      }
      // freeze 冻结一个对象使其不可修改 且freeze为浅冻结
      // mdn 地址 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
      if (__DEV__) {
        if (Object.freeze) {
          Object.freeze(childrenArray);
        }
      }
      props.children = childrenArray;
    }
复制代码
  1. 处理 defaultProps
// 若是是组件
    if (type && type.defaultProps) {
      const defaultProps = type.defaultProps;
      for (propName in defaultProps) {
        if (props[propName] === undefined) {
          props[propName] = defaultProps[propName];
        }
      }
    }
复制代码
  1. __DEV__环境下key,ref处理
// __DEV__下warning 在displayName 组件中 key,ref 是不可配置的属性
    if(__DEV__){
      if(key || ref){
        const displayName = 
        typeof type === 'function' 
        ? type.displayName || type.name || 'Unknown'
        : type;
        if (key) {
          defineKeyPropWarningGetter(props, displayName);
        }
        if (ref) {
          defineRefPropWarningGetter(props, displayName);
        }
      }
    }
复制代码
  1. 返回结果
return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
复制代码

ReactElement

ReactElement 接收type, key, ref, self, source, owner, props 7各参数api

slef:
当React.createElement被调用时,用来检测this的来源,以便咱们能够发出警告。咱们想要摆脱全部者并用箭头函数替换字符串refs,只要this和全部者是相同的就没有了改变行为 source:
一个注解对象 (由转路器或者其余人添加)。用来判断两个react element元素是否相等 owner:
记录负责建立此元素的组件数组

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // $$typeof是区分react Element对象的惟一标识
    $$typeof: REACT_ELEMENT_TYPE,

    // 属于元素的内置属性
    type: type,
    key: key,
    ref: ref,
    props: props,

    // 记录负责建立此元素的组件.
    _owner: owner,
  };

  if (__DEV__) {
    element._store = {};

    Object.defineProperty(element._store, 'validated', {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false,
    });
    // self and source are DEV only properties.
    Object.defineProperty(element, '_self', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self,
    });
    // 为了测试目的,在两个不一样的地方建立的两个元素应被视为相等,所以咱们将其隐藏在枚举中
    Object.defineProperty(element, '_source', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source,
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }

  return element;
};

复制代码

React Component

React Component 只是用来帮助咱们承载一些信息的,没有生命周期等功能的实现promise

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
复制代码

Componenet的prototype 挂载方法bash

  1. setState
  2. forceUpdate

PureComponent

PureComponent 进行的 浅比较 (shallow equality check)babel

function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// PureComponent 与 Component 本文件中代码上区别是
pureComponentPrototype.isPureReactComponent = true;
复制代码

ref

三种使用ref的方式dom

  1. sting ref
  2. method ref
  3. object ref
    代码不多 只有这么点

createRef

import type {RefObject} from 'shared/ReactTypes';

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {
    Object.seal(refObject);
  }
  return refObject;
}
复制代码

forwardRef

forwardRef 解决了 pure Function 没有ref实例的问题
forwardRef 返回 React$Node
官网api文档 Refs 转发
点击进入 : 在线代码demo编辑

Suspense && lazy

点击进入 Suspense && lazy 使用demo 连接

import React, { Suspense } from "react";
import ReactDOM from "react-dom";
const LazyComp = React.lazy(() => import("./lazy"));

let data = "";
let promise = "";
const requestData = () => {
  if (data) return data;
  if (promise) throw promise;
  promise = new Promise(res => {
    setTimeout(() => {
      data = "data res";
      res();
    }, 2000);
  });
  throw promise;
};

const SuspenseComp = () => {
  const data = requestData();
  return <div>{data}</div>;
};
const Comp = () => {
  return (
    <Suspense fallback="loading data"> <SuspenseComp /> <LazyComp /> </Suspense>
  );
};

ReactDOM.render(<Comp />, document.getElementById("container")); 复制代码

Suspense只有等内部全部组件都加载完毕,才会把fallback的内容去掉,有任何一个组件处于pending状态,都会加载fallback

Children

点击 进入下文代码在线编辑demo

React.children.map把多维数组A转一维数B,props.children的每一项在数组B中遍历

下文源代码中 result

function mapChildren(children, func, context) {
  if (children == null) {
    return children;
  }
  const result = [];
  mapIntoWithKeyPrefixInternal(children, result, null, func, context);
  return result;
}
复制代码
相关文章
相关标签/搜索