阅读 126

React源码学习系列(一)

本文记录一些react17.0.2源码学习过程中的一些知识。

入口目录

  • react-main

    • packages

      • react

        • index.js

1. React.Component 和 React.PureComponent

  • 所在目录

    • react-main

      • packages

        • react

          • src

            • ReactBaseClasses.js

  • 定义Component

/**
 * Base class helpers for the updating state of a component.
 */
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;
}
复制代码
  • isReactComponent判断是否是React的Component
Component.prototype.isReactComponent = {};
复制代码
  • 定义setState函数

参数partialState可以是object,也可以是function,最后通过updater.enqueueSetState执行回调函数callback。

Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
复制代码
  • 定义forceUpdate函数

redux状态管理中的state发生改变,通过 forceUpdate 函数强制对页面的进行render。

Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
复制代码
  • 定义PureComponent
function PureComponent(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;
  this.updater = updater || ReactNoopUpdateQueue;
}
复制代码
  • Component和PureComponent的关系

创建一个ComponentDummy类,Component原型赋值给ComponentDummy原型

PureComponent原型继承自ComponentDummy的实例,即new ComponentDummy()

因此,PureComponent类是通过原型式继承自Component类

PureComponent内置了shouldComponentUpdate生命周期。

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;


const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;


Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
复制代码

2. React.createElement & React.createFactory & React.cloneElement & React.isValidElement

  • 所在目录

    • react-main

      • packages

        • react

          • src

            • ReactElement.js
  • ReactElement 用来处理传入进来的元素的各个属性,拼装输出一个React元素

const ReactElement = function(type, key, ref, self, source, owner, props) {
    const element = {
        // $$typeof为 symbolFor('react.element') 用来识别当前元素是React元素
        $$typeof: REACT_ELEMENT_TYPE,
        
        type: type,  // type为标签类型 div, p 等
        
        key: key,  // 用来调和DOM Diff
        // createElement的第二个参数 props => { id: 1, text: 'test' } 作为属性加到创建出来的React元素上
        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,
        });
        // Two elements created in two different places should be considered
        // equal for testing purposes and therefore we hide it from enumeration.
        Object.defineProperty(element, '_source', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: source,
        });
        if (Object.freeze) {
            Object.freeze(element.props);
            Object.freeze(element);
        }
    }
    return element;
}
复制代码
  • createElement 用来生成React元素
/**
 * Create and return a new ReactElement of the given type.
*/
export function createElement(type, config, children) {

    let propName;
    
    // Reserved names are extracted
    const props = {};
    
    let key = null;
    let ref = null;
    let self = null;
    let source = null;
    
    // 传入createElement的属性不为空
    if (config != null) {
        
        // 传入的属性中有有效的Ref
        
        if (hasValidRef(config)) {
            ref = config.ref;

            if (__DEV__) {
                warnIfStringRefCannotBeAutoConverted(config);
            }
        }
        // 传入的属性有有效的Key,存到key上
        
        if (hasValidKey(config)) {
            key = '' + config.key;
        }
        
        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        
        for (propName in config) {
            if (
                hasOwnProperty.call(config, propName) &&
                !RESERVED_PROPS.hasOwnProperty(propName)
            ) {
                props[propName] = config[propName];
            }
        }
        
    }
    
    // Children可能是多个参数
    
    const childrenLength = arguments.length - 2;
    if (childrenLength === 1) {
    
        props.children = children;  // children会放到props上
        
    } else if (childrenLength > 1) {
    
        const childArray = Array(childrenLength);
        
        for (let i = 0; i < childrenLength; i++) {
            childArray[i] = arguments[i + 2];
        }
        
        props.children = childArray;
    }
    
    return ReactElement(
        type,
        key,
        ref,
        self,
        source,
        ReactCurrentOwner.current,
        props,
    );
}
复制代码

最终输出的JS对象element

{
    $$typeof: symbolFor('react.element'), // React元素标识
    key: config.key, // key属性用来调和diff
    ref: config.ref, // ref属性用来获取渲染后的DOM节点
    props: {
        children: {} // {} | [],
        ... // 以及传入飞config中所有的属性
    },
    type: 'div',  // 元素类型
    _owner: null, // 谁负责创建出这个元素,默认为Fiber
    _store: null, // dev环境下的做的备份
    _self: null,
    _source: null
}
复制代码
  • createFactory 创建一个type类型的factory方法
/**
 * Return a function that produces ReactElements of a given type.
*/
export function createFactory(type) {
  const factory = createElement.bind(null, type);
  // Expose the type on the factory and the prototype so that it can be
  // easily accessed on elements. E.g. `<Foo />.type === Foo`.
  // This should not be named `constructor` since this may not be the function
  // that created the element, and it may not even be a constructor.
  // Legacy hook: remove it
  factory.type = type;
  return factory;
}
复制代码
  • cloneElement 使用元素作为起点克隆并且返回一个新的ReactElement
/**
 * Clone and return a new ReactElement using element as the starting point.
 * See https://reactjs.org/docs/react-api.html#cloneelement
 */
export function cloneElement(element, config, children) {

}
复制代码
  • isValidElement 用来验证一个对象是否是有效的ReactElement元素
/**
 * Verifies the object is a ReactElement.
*/
export function isValidElement(object) {
  return (
    typeof object === 'object' &&
    object !== null &&
    object.$$typeof === REACT_ELEMENT_TYPE
  );
}
复制代码

3. React.Children

  • React.children 是react提供的一个处理this.props.children的方法,无论this.props.chilren是undefinedobject(一个child),array(多个child),都不用担心,它都做了兼容的处理,直接传入React.Children方法中即可返回出React的节点。
<script type="text/jsx">
  var List = React.createClass({
    render: function() {
      return (
        <ul>
          {
              React.Children.map(this.props.children, function (child) {
                  return <li>{child}</li>;
              })
          }
        </ul>
      );
    }
  });
 
  React.render(
    <List>
      <span>hello</span>
      <span>world</span>
    </List>,
    document.body
  );
</script>
复制代码
  • React.Children.map 遍历 this.props.children 返回HTML结构

  • React.Children.forEach 遍历 this.props.children 没有返回值

  • React.Children.count 返回 this.props.children 子元素总和

<List>
    <span>hello</span>
    <span>world</span>
</List>
console.log(React.Children.count(this.props.children)); // 2
 
<List></List>
console.log(React.Children.count(this.props.children)); // 0
 
<List>null</List>
console.log(React.Children.count(this.props.children)); // 1
复制代码
  • React.Children.only

    • 返回 this.props.children 中 仅有的子级。否则抛出异常。

    • 这里仅有的子级,only方法接受的参数只能是一个对象,不能是多个对象(数组)。

4. React.createRef

  • 所在目录

    • react-main

      • packages

        • react

          • src

            • ReactCreateRef.js
  • createRef 创建一个引用对象,引用对象是父组件的成员,父组件可以通过引用对象操作子组件

// an immutable object with a single mutable value
export function createRef(): RefObject {
  const refObject = {
    current: null,
  };
  if (__DEV__) {   // 将对象密封(不能增删属性、配置属性,但可以给属性赋值)
    Object.seal(refObject);
  }
  return refObject;
}
复制代码

5. React.createContext

  • createContext 创建一个上下文,该context对象有 Provide 和 Consumer 属性
export function createContext<T>(defaultValue: T): ReactContext<T> {

  const context: ReactContext<T> = {
      $$typeof: REACT_CONTEXT_TYPE,
      _currentValue: defaultValue,
      _currentValue2: defaultValue,
      _threadCount: 0,
      Provider: (null: any),
      Consumer: (null: any),
  };
  
  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };
  
  let hasWarnedAboutUsingNestedContextConsumers = false;
  let hasWarnedAboutUsingConsumerProvider = false;
  let hasWarnedAboutDisplayNameOnConsumer = false;
  
  if (__DEV__) {
  
      const Consumer = {
          $$typeof: REACT_CONTEXT_TYPE,
          _context: context,
      };
      
      Object.defineProperties(Consumer, {
          Provider: {
              get() {
                  if (!hasWarnedAboutUsingConsumerProvider) {
                      hasWarnedAboutUsingConsumerProvider = true;
                      return context.Provider;
              },
              set(_Provider) {
                  context.Provider = _Provider;
              },
          },
          _currentValue: {
              get() {
                  return context._currentValue;
              },
              set(_currentValue) {
                  context._currentValue = _currentValue;
              },
          },
          _currentValue2: {
              get() {
                  return context._currentValue2;
              },
              set(_currentValue2) {
                  context._currentValue2 = _currentValue2;
              },
          },
          _threadCount: {
              get() {
                  return context._threadCount;
              },
              set(_threadCount) {
                  context._threadCount = _threadCount;
              },
          },
          Consumer: {
              get() {
                  if (!hasWarnedAboutUsingNestedContextConsumers) {
                      hasWarnedAboutUsingNestedContextConsumers = true;
                  }
                  return context.Consumer;
              },
          },
          displayName: {
              get() {
                  return context.displayName;
              },
              set(displayName) {
                  if (!hasWarnedAboutDisplayNameOnConsumer) {
                      hasWarnedAboutDisplayNameOnConsumer = true;
                  }
              },
          },
      });
      
      context.Consumer = Consumer;
  
   } else {
   
      context.Consumer = context;
      
   }
   
   if (__DEV__) {
      context._currentRenderer = null;
      context._currentRenderer2 = null;
   }

   return context;

}
复制代码

6. React.lazy

7. React.memo

参考文章

React Hook 中 createContext & useContext 跨组件透传上下文与性能优化

文章分类
前端
文章标签