在线电子书
参考

一、基础

1. React API

看一下 React 暴露出来的 API:

  1. const React = {
  2. Children: {
  3. map,
  4. forEach,
  5. count,
  6. toArray,
  7. only,
  8. },
  9. createRef,
  10. Component,
  11. PureComponent,
  12. createContext,
  13. forwardRef,
  14. lazy,
  15. memo,
  16. Fragment: REACT_FRAGMENT_TYPE,
  17. StrictMode: REACT_STRICT_MODE_TYPE,
  18. Suspense: REACT_SUSPENSE_TYPE,
  19. createElement: __DEV__ ? createElementWithValidation : createElement,
  20. cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
  21. createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
  22. isValidElement: isValidElement,
  23. version: ReactVersion,
  24. __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
  25. };

Children

官方提供的一些用于处理 props.children 的方法

createRef

新的ref用法,React即将抛弃 <div ref="myDiv" /> 这种 string ref 的用法,将来你只能使用两种方式来使用 ref

  1. class App extends React.Component{
  2. constructor() {
  3. this.ref = React.createRef()
  4. }
  5. render() {
  6. return <div ref={this.ref} />
  7. // or
  8. return <div ref={(node) => this.funRef = node} />
  9. }
  10. }

或者使用函数组件

  1. const App = () => {
  2. const inputEl = useRef(null)
  3. return (
  4. <input ref={inputEl} type="text" />
  5. )
  6. }

Component & PureComponent

这两个类基本相同,唯一的区别是PureComponent的原型上多了一个标识

  1. if (ctor.prototype && ctor.prototype.isPureReactComponent) {
  2. return (
  3. !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
  4. );
  5. }

这是检查组件是否需要更新的一个判断,ctor 就是你声明的继承自 Component or PureComponent 的类,他会判断你是否继承自 PureComponent,如果是的话就 shallowEqual 比较 state 和 props

PureComponent优点

继承PureComponent的组件,若stateprops前后不改变,则不重新渲染。

Component优点

灵活性大,可以自由根据实际情况在shouldComponentUpdate生命周期中进行优化

createElement & cloneElement & createFactory & isValidElement

createElement

它可谓是 React 中最重要的 API 了,他是用来创建 ReactElement 的,但是多数人却从没见过也没用过,这是为啥呢?因为你用了 JSX,JSX 并不是标准的 js,所以要经过编译才能变成可运行的 js,而编译之后,createElement 就出现了:

  1. // jsx
  2. <div id="app">content</div>
  3. // js
  4. React.createElement('div', { id: 'app' }, 'content')

cloneElement

用于克隆一个 ReactElement

createFactory

是专门用来创建某一类 ReactElement 的工厂

isValidElement

用来验证是否是一个 ReactElement新的ref用法,React即将抛弃

2. React Element

该部分详细源码如下:

  1. /**
  2. * 创建并返回指定类型的 ReactElement
  3. * @param {*} type - 可以是标签名字符串(如 'div' 或 'span'),也可以是 React 组件类型(class 组件或函数组件),或是 React fragment 类型
  4. * @param {*} config - 配置项,标签的 attribute
  5. * @param {*} children - 子元素
  6. */
  7. export function createElement(type, config, children) {
  8. let propName;
  9. // Reserved names are extracted
  10. const props = {};
  11. let key = null;
  12. let ref = null;
  13. let self = null;
  14. let source = null;
  15. if (config != null) {
  16. // 单独处理 key 和 ref
  17. if (hasValidRef(config)) {
  18. ref = config.ref;
  19. }
  20. if (hasValidKey(config)) {
  21. key = '' + config.key;
  22. }
  23. self = config.__self === undefined ? null : config.__self;
  24. source = config.__source === undefined ? null : config.__source;
  25. // Remaining properties are added to a new props object
  26. // 存储除内置 props 外的其他 props
  27. for (propName in config) {
  28. if (
  29. hasOwnProperty.call(config, propName) &&
  30. !RESERVED_PROPS.hasOwnProperty(propName)
  31. ) {
  32. props[propName] = config[propName];
  33. }
  34. }
  35. }
  36. // Children can be more than one argument, and those are transferred onto
  37. // the newly allocated props object.
  38. // 将一个或多个 children,即第三个及以后的参数,处理后赋值给 props.children
  39. const childrenLength = arguments.length - 2;
  40. if (childrenLength === 1) {
  41. props.children = children;
  42. } else if (childrenLength > 1) {
  43. const childArray = Array(childrenLength);
  44. for (let i = 0; i < childrenLength; i++) {
  45. childArray[i] = arguments[i + 2];
  46. }
  47. props.children = childArray;
  48. }
  49. // Resolve default props
  50. // 若该类型存在默认的 props, 并且未进行配置,则按默认的进行赋值
  51. if (type && type.defaultProps) {
  52. const defaultProps = type.defaultProps;
  53. for (propName in defaultProps) {
  54. if (props[propName] === undefined) {
  55. props[propName] = defaultProps[propName];
  56. }
  57. }
  58. }
  59. return ReactElement(
  60. type,
  61. key,
  62. ref,
  63. self,
  64. source,
  65. ReactCurrentOwner.current,
  66. props,
  67. );
  68. }

ReactElement 方法最终返回的结果是一个对象

  1. const ReactElement = function(type, key, ref, self, source, owner, props) {
  2. const element = {
  3. // This tag allows us to uniquely identify this as a React Element
  4. // 用于标示 element 是什么类型
  5. $$typeof: REACT_ELEMENT_TYPE,
  6. // Built-in properties that belong on the element
  7. type: type,
  8. key: key,
  9. ref: ref,
  10. props: props,
  11. // Record the component responsible for creating this element.
  12. _owner: owner,
  13. };
  14. return element;
  15. };

3. React Component

  1. class Welcome extends React.Component {
  2. constructor(props) {
  3. super(props)
  4. this.state = {}
  5. }
  6. componentDidMount(){}
  7. render() {
  8. return <h1>Hello, {this.props.name}</h1>;
  9. }
  10. }

以上是我们使用类组件时的常见使用方式,主要使用 Component 和 PureComponent 这两个基类,该部分详细源码如下:

React.component

  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. import invariant from 'shared/invariant';
  8. import lowPriorityWarning from 'shared/lowPriorityWarning';
  9. import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
  10. const emptyObject = {};
  11. if (__DEV__) {
  12. Object.freeze(emptyObject);
  13. }
  14. /**
  15. * Base class helpers for the updating state of a component.
  16. */
  17. function Component(props, context, updater) {
  18. this.props = props;
  19. this.context = context;
  20. // If a component has string refs, we will assign a different object later.
  21. this.refs = emptyObject;
  22. // We initialize the default updater but the real one gets injected by the
  23. // renderer.
  24. this.updater = updater || ReactNoopUpdateQueue;
  25. }
  26. // 给 Components 赋值以做标记
  27. Component.prototype.isReactComponent = {};
  28. /**
  29. * 给 Component 的原型增加 setState-更新状态 方法
  30. * @param {object|function} partialState - 更新的状态值, 可以对象或函数,将和当前 state 合并,生成下一个状态
  31. * @param {?function} callback - 回调函数,用于 state 更新完成后执行的回调函数
  32. */
  33. Component.prototype.setState = function(partialState, callback) {
  34. // 判断 partialState 是否符合条件
  35. invariant(
  36. typeof partialState === 'object' ||
  37. typeof partialState === 'function' ||
  38. partialState == null,
  39. 'setState(...): takes an object of state variables to update or a ' +
  40. 'function which returns an object of state variables.',
  41. );
  42. // 这里是 state 更新机制的重点
  43. // 具体实现在 react-dom 中,不在 react
  44. this.updater.enqueueSetState(this, partialState, callback, 'setState');
  45. };
  46. /**
  47. * 给 Component 的原型增加 forceUpdate-强制更新 方法,用于强制更新场景,无论 props 和 state 是否改变
  48. * @param {?function} callback - 回调函数,用于更新完成后执行的回调函数
  49. */
  50. Component.prototype.forceUpdate = function(callback) {
  51. this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
  52. };

重点:

  1. Component() 方法本质是一个类,多用于继承
  2. setState() 和 forceUpdate 是 Component 原型上的方法,其本质是调用 ReactNoopUpdateQueue.js 中的 enqueueSetState() 方法
  3. React.Component() 只涉及了props、context、refs、updater、isReactComponent、setState、forceUpdate,其他均没有自己实现

    React.PureComponent

    如果一个组件的渲染只依赖于外界传进去的 props 和自己的 state,而并不依赖于其他的外界的任何数据,也就是说像纯函数一样,给它什么,它就吐出(渲染)什么出来。这种组件的复用性是最强的。这里的 PureComponent 便是如此使用的,它只有在 props 或 state 改变时才会重新渲染 ```javascript // 通过借用构造函数,实现典型的寄生组合式继承,避免原型污染 function ComponentDummy() {} ComponentDummy.prototype = Component.prototype;

/**

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

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); pureComponentPrototype.constructor = PureComponent; // 无须再通过原型链再向上找一级. Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent}; ``` 重点:
1. 借助空方法 ComponentDummy 使用典型的寄生组合式继承方式,避免原型污染
2. 使用 Object.assign(pureComponentPrototype, Component.prototype) 复制prototype,便可以不用再去 Component 的原型中找方法,减少了一次原型链的查找

参考:
https://segmentfault.com/a/1190000019783986?utm_source=tag-newest
https://www.cnblogs.com/heyushuo/p/10057676.html