react学习笔记一

263 阅读1分钟

前言

最近开始接触react开发,根据官网文档和react源码,记录学习过程中的一些知识点。

准备工作

创建一个index.html文件,然后直接引入react的相关js文件,通过debug的方式调试源码


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>react-demon</title>
  <script src="<https://unpkg.com/react@17/umd/react.development.js>" crossorigin></script>
  <script src="<https://unpkg.com/react-dom@17/umd/react-dom.development.js>" crossorigin></script>
</head>
<body>
  <script>
    var app = document.createElement('div');
    document.body.appendChild(app);
    console.log('react', React);
    ReactDOM.render(
      React.createElement(
        'div',
        null,
        'Hello World'
      ),
      app
    );
  </script>
</body>
</html>

React api 总览

  exports.Children = Children;
  exports.Component = Component;
  exports.PureComponent = PureComponent;
  exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals$1;
  exports.cloneElement = cloneElement$1;
  exports.createContext = createContext;
  exports.createElement = createElement$1;
  exports.createFactory = createFactory;
  exports.createRef = createRef;
  exports.forwardRef = forwardRef;
  exports.isValidElement = isValidElement;
  exports.lazy = lazy;
  exports.memo = memo;
  exports.version = ReactVersion;

Children

Children是用来处理props.children 的一个对象, Children 对象里面包含了mapforEachcounttoArrayonly等一些方法,处理children可以直接通过React.Children.方法

var Children = {
    map: mapChildren,
    forEach: forEachChildren,
    count: countChildren,
    toArray: toArray,
    only: onlyChild
  };

Component

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

Component.prototype.isReactComponent = {}

实例上挂载的方法

实例方法作用
isReactComponent是否是react组件
setState设置状态
forceUpdate强制更新状态

PureComponent

Component基本一样,会合并两个实例对象

区别:在props,state改变时,PureComponent会进行一个浅比较,即实现了shouldComponentUpdate方法。

/**
   * Convenience component with default shallow equality check for sCU.
   */
// 源码
  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;
  }

  var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
  pureComponentPrototype.constructor = PureComponent; // Avoid an extra prototype jump for these methods.

  assign(pureComponentPrototype, Component.prototype);

  pureComponentPrototype.isPureReactComponent = true;

tip:如果赋予组件相同的propsstate,render()函数会渲染相同的内容,PureComponent性能会更高。另外由于PureComponent只是对对象的浅层比较,会跳过子组件组件树的prop更新,因此需确保子组件也是纯组件。

__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED


没有文档的API和内部数据结构,因为后续可能更改变动,不建议使用。

cloneElement

element元素为样板克隆,并返回新的React元素

// 源码
function cloneElementWithValidation(element, props, children) {
    var newElement = cloneElement.apply(this, arguments);

    for (var i = 2; i < arguments.length; i++) {
      validateChildKeys(arguments[i], newElement.type);
    }

    validatePropTypes(newElement);
    return newElement;
  }
var cloneElement$1 =  cloneElementWithValidation ;

createContext

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

createElement

创建并返回指定类型的新的React元素。入参是type,props,children

// 源码
function createElementWithValidation(type, props, children) {
    var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to
    // succeed and there will likely be errors in render.
		// ……
    return element;
  }
var cloneElement$1 =  cloneElementWithValidation ;

其中type值可以标签名字符串(如 'div' 或 'span'),也可以是 React组件类型 (class 组件或函数组件),或是 Fragment,Profiler等类型。

// 源码
function isValidElementType(type) {
    if (typeof type === 'string' || typeof type === 'function') {
      return true;
    } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).

    if (type === exports.Fragment || type === exports.Profiler || type === REACT_DEBUG_TRACING_MODE_TYPE || type === exports.StrictMode || type === exports.Suspense || type === REACT_SUSPENSE_LIST_TYPE || type === REACT_LEGACY_HIDDEN_TYPE || enableScopeAPI ) {
      return true;
    }

    if (typeof type === 'object' && type !== null) {
      if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || type.$$typeof === REACT_FUNDAMENTAL_TYPE || type.$$typeof === REACT_BLOCK_TYPE || type[0] === REACT_SERVER_BLOCK_TYPE) {
        return true;
      }
    }

    return false;
  }

createFactory

返回用于生成指定类型的React元素的函数。该 方法内部是挂载createElementWithValidation函数的属性方法,入参是type,其中type值同createElement方法类型。此辅助函数已废弃,建议使用 JSX 或直接调用 React.createElement() 来替代它

// 源码
function createFactoryWithValidation(type) {
    var validatedFactory = createElementWithValidation.bind(null, type);
    validatedFactory.type = type;
		// ……
    return validatedFactory;
  }
var createFactory =  createFactoryWithValidation ;

createRef

创建一个能够通过 ref 属性附加到 React 元素的 ref

// 源码
function createRef() {
    var refObject = {
      current: null
    };

    {
      Object.seal(refObject);
    }

    return refObject;
  }

forwardRef

创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中,

此方法入参是是一个渲染函数, reactprops, ref作为参数来调用此函数

// 用法
const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
// 源码
function forwardRef(render) {
    // ...

    var elementType = {
      $$typeof: REACT_FORWARD_REF_TYPE,
      render: render
    };

    {
      var ownName;
      Object.defineProperty(elementType, 'displayName', {
        enumerable: false,
        configurable: true,
        get: function () {
          return ownName;
        },
        set: function (name) {
          ownName = name;

          if (render.displayName == null) {
            render.displayName = name;
          }
        }
      });
    }

    return elementType;
  }

isValidElement

返回值是一个Boolean值,验证是否为React元素,入参是一个object对象,判断类型是否是react自定义的属性REACT_ELEMENT_TYPE

var REACT_ELEMENT_TYPE = 0xeac7;
REACT_ELEMENT_TYPE = symbolFor('react.element');

function isValidElement(object) {
    return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
  }

lazy

通过此方法可以定义一个动态组件,有助于减少代码提及大小,且只有在使用的时候才会去加载,改属性需要<React.Suspense>组件配合使用。lazy动态加载的本质是通过Promise的方式异步操作的,具体可以查看lazyInitializer方法

// example
// 这个组件是动态加载的
const SomeComponent = React.lazy(() => import('./SomeComponent'));
// 源码
function lazy(ctor) {
    var payload = {
      // We use these fields to store the result.
      _status: -1,
      _result: ctor
    };
    var lazyType = {
      $$typeof: REACT_LAZY_TYPE,
      _payload: payload,
      _init: lazyInitializer
    };
		// ....
    return lazyType;
  }
// 异步加载
function lazyInitializer(payload) {
    if (payload._status === Uninitialized) {
      var ctor = payload._result;
      var thenable = ctor(); // Transition to the next state.

      var pending = payload;
      pending._status = Pending;
      pending._result = thenable;
      thenable.then(function (moduleObject) {
        if (payload._status === Pending) {
          var defaultExport = moduleObject.default;

          {
            if (defaultExport === undefined) {
              error('lazy: Expected the result of a dynamic import() call. ' + 'Instead received: %s\n\nYour code should look like: \n  ' + // Break up imports to avoid accidentally parsing them as dependencies.
              'const MyComponent = lazy(() => imp' + "ort('./MyComponent'))", moduleObject);
            }
          } // Transition to the next state.

          var resolved = payload;
          resolved._status = Resolved;
          resolved._result = defaultExport;
        }
      }, function (error) {
        if (payload._status === Pending) {
          // Transition to the next state.
          var rejected = payload;
          rejected._status = Rejected;
          rejected._result = error;
        }
      });
    }

    if (payload._status === Resolved) {
      return payload._result;
    } else {
      throw payload._result;
    }
  }

memo

用此方法,如果组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。由于该方法只检查props变更,当state 或 context发生变化时,仍会重新渲染。

// type为type值可以标签名字符串(如 'div' 或 'span'),
// 也可以是 React组件类型 (class 组件或函数组件),
// 或是 Fragment,Profiler等类型
// compare 可以为一个函数可以控制对比过程
function memo(type, compare) {
    {
      if (!isValidElementType(type)) {
        error('memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type);
      }
    }

    var elementType = {
      $$typeof: REACT_MEMO_TYPE,
      type: type,
      compare: compare === undefined ? null : compare
    };

    {
      var ownName;
      Object.defineProperty(elementType, 'displayName', {
        enumerable: false,
        configurable: true,
        get: function () {
          return ownName;
        },
        set: function (name) {
          ownName = name;

          if (type.displayName == null) {
            type.displayName = name;
          }
        }
      });
    }

    return elementType;
  }

结尾

后续会一直总结和记录在学习react中一些知识点,以及项目开发中碰见的问题。