【基础】React 官网—高级指引

209 阅读14分钟

React 18.2.0 已经发布很久了,这几天抽空重新完整的过了一遍 React的官网,并结合当时学习React 16.8 的总结,将笔记和案例记录下来,方便以后查阅迭代

总的来说react的语法不算太多,打算按照 React 官网 文档的教程顺序,从 安装 -> 核心概念 -> 高级指引 -> hook 开始。

本文档会结合一些案例(基于react18),方便大家阅读和实践,以备 开箱即用

仓库地址:PantherVkin/react-note

无障碍

无障碍辅助功能 – React (reactjs.org)

代码分割

代码分割 – React (reactjs.org)

Context

React中的上下文特点:

  1. 当某个组件创建了上下文后,上下文中的数据,会被所有后代组件共享

  2. 如果某个组件依赖了上下文,会导致该组件不再纯粹(外部数据仅来源于属性props)

  3. 一般情况下,用于第三方组件(通用组件)

上下文的数据变化:

上下文中的数据不可以直接变化,最终都是通过状态改变;

在上下文中加入一个处理函数,可以用于后代组件更改上下文的数据;

React.createContext(默认值)

创建上下文:

上下文是一个独立于组件的对象,该对象通过React.createContext(默认值)创建。

返回的是一个包含两个属性的对象。

  1. Provider属性

上下文生产者:是一个组件,该组件会创建一个上下文

上下文赋值:该组件有一个value属性,通过该属性,可以为其数据赋值。

  • 类组件中,直接使用this.context获取上下文数据

要求:必须拥有静态属性 contextType , 应赋值为创建的上下文对象。

    <script type="text/babel">
      'use strict';

      // 1. 创建上下文
      const ctx = React.createContext();
      class A extends React.Component {
        state = {
          name: 'ACtx',
          desc: 'A组件创建的上下文'
        };

        render() {
          // 2. ctx.Provider组件创建上下文
          // value 上下文赋值
          return (
            <ctx.Provider value={this.state}>
              <div>
                <h1>Title: A</h1> {this.props.children}           
              </div>
            </ctx.Provider>
          );
        }
      }

      // 3. 在`类组件`中,直接使用`this.context`获取上下文数据
      // 要求:必须拥有静态属性 `contextType` , 应赋值为创建的上下文对象。
      class BChild extends React.Component {
        static contextType = ctx;
        render() {
          return (
            <div>
              <h2>Title: B</h2>
              <p>来自A的上下文数据:{this.context.name}</p>       
            </div>
          );
        }
      }

      function App() {
        return (
          <div className="App">
            <A>
              <BChild></BChild>           
            </A>
          </div>
        );
      }

      const root = ReactDOM.createRoot(document.querySelector('#app'));
      root.render(
        <div>
           <App />
        </div>
      );
    </script>

image.png

react-note/1.类组件使用上下文中的数据.html at master · PantherVkin/react-note (github.com)

  1. Consumer属性

消费者组件,使用上下文中的数据

  • 在函数组件中,需要使用Consumer来获取上下文数据

Consumer是一个组件;

它的子节点,是一个函数(它的props.children需要传递一个函数),上下文通过参数传递给函数,返回结果会渲染到页面;

      // 3. 使用`Consumer`来获取上下文数据
      function BChild() {
        return (
          <div>
            <ctx.Consumer>
              {(value) => {
                return (
                  <div>
                    <h2>Title: B</h2>
                    <p>来自A的上下文数据:{value.name}</p>               
                  </div>
                );
              }}
            </ctx.Consumer>
               
          </div>
        );
      }

      function App() {
        return (
          <div className="App">
            <A>
               <BChild></BChild>       
            </A>
          </div>
        );
      }

react-note/2.函数组件中使用 consumer.html at master · PantherVkin/react-note (github.com)

  • class中使用 Consumer
      // 3. 使用`Consumer`来获取上下文数据
      class BChild extends React.Component {
        render() {
          return (
            <div>
              <ctx.Consumer>
                {(value) => {
                  console.log('v', value);
                  return (
                    <div>
                      <h2>Title: B</h2> <p>来自A的上下文数据:{value.name}</p>                   
                    </div>
                  );
                }}
              </ctx.Consumer>
            </div>
          );
        }
      }

react-note/3.类组件中使用 consumer.html at master · PantherVkin/react-note (github.com)

注意细节

  1. 强制刷新问题?

如果,上下文提供者(Context.Provider)中的value属性发生变化(Object.is比较),会导致该上下文提供的所有后代元素全部重新渲染(强制更新),无论该子元素是否有优化(无论shouldComponentUpdate函数返回什么结果)。

    <script type="text/babel">
      'use strict';

      const ctx = React.createContext();
      class A extends React.Component {
        state = {
          name: 'ACtx',
          desc: 'A组件创建的上下文'
        };

        changeContext = () => {
          this.setState({
            name: 'ACtx',
            desc: 'A组件创建的上下文'
          });
        };
        render() {
          // ctx.Provider组件创建上下文
          // value 上下文赋值
          return (
            <ctx.Provider value={this.state}>
              <div>
                <h1>Title: A</h1> <button onClick={this.changeContext}>改变上下文</button>
                {this.props.children}
              </div>
            </ctx.Provider>
          );
        }
      }

      class BChild extends React.Component {
        static contextType = ctx;
        shouldComponentUpdate(nextProps, nextState) {
          console.log('运行了优化');
          return false;
        }
        render() {
          console.log('childB render');
          return (
            <h1>
              name: {this.context.name},desc: {this.context.desc}
            </h1>
          );
        }
      }

      function App() {
        return (
          <div className="App">
            <A>
              <BChild></BChild>
            </A>
          </div>
        );
      }

      const root = ReactDOM.createRoot(document.querySelector('#app'));
      root.render(
        <div>
          <App />
        </div>
      );
    </script>

image.png

react-note/4.注意.html at master · PantherVkin/react-note (github.com)

  • 解决思路

为 state 嵌套一层。

react-note/5.注意-解决.html at master · PantherVkin/react-note (github.com)

错误边界

错误边界 – React (reactjs.org)

默认情况下,若一个组件在渲染期间(render)发生错误,会导致整个组件树全部被卸载。

部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界

错误边界是一种 React 组件,该组件会捕获到渲染期间(render)子组件(渲染期间、生命周期方法以及构造函数)发生的错误,并有能力阻止错误继续传播。

  1. 案例:不使用错误边界时

react-note/1.不使用错误边界时.html at master · PantherVkin/react-note (github.com)

错误边界

如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。

当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

  1. 编写生命周期函数 getDerivedStateFromError
  • 静态函数

  • 运行时间点:渲染子组件的过程中,发生错误之后,在更新页面之前

  • 只有子组件发生错误,才会运行该函数

  • 该函数返回一个对象,React会将该对象的属性覆盖掉当前组件的state

  • 参数:错误对象

  • 通常,该函数用于改变状态

  1. 编写生命周期函数 componentDidCatch
  • 实例方法

  • 运行时间点:渲染子组件的过程中,发生错误,更新页面之后,由于其运行时间点比较靠后,因此不太会在该函数中改变状态

  • 通常,该函数用于记录错误消息

    <script type="text/babel">
      'use strict';
      class Child extends React.PureComponent {
        state = {
          count: 0
        };

        handleClick = () => {
          this.setState({
            count: this.state.count + 1
          });
        };

        render() {
          if (this.state.count === 5) {
            throw new Error('count Error');
          }
          return (
            <div>
              <button onClick={this.handleClick}>点击+1</button> <h2>{this.state.count}</h2>
            </div>
          );
        }
      }

      class ErrorBound extends React.PureComponent {
        state = {
          hasError: false
        };

        static getDerivedStateFromError(error) {
          console.log('发生错误了', error);
          return {
            hasError: true
          };
        }

        componentDidCatch(error, info) {
          console.log('记录错误信息');
        }

        render() {
          if (this.state.hasError) {
            return <h1>出现错误了!</h1>;
          }
          return this.props.children;
        }
      }

      function App() {
        return (
          <div className="App">
            <ErrorBound>
              <Child></Child>
            </ErrorBound>
            <h2>另一个组件</h2>
          </div>
        );
      }

      const root = ReactDOM.createRoot(document.querySelector('#app'));
      root.render(
        <div>
          <App />
        </div>
      );
    </script>

react-note/1.使用错误边界时.html at master · PantherVkin/react-note (github.com)

注意

  1. 错误边界无法捕获以下场景中产生的错误:
  • 事件处理(了解更多

  • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)

  • 服务端渲染

  • 它自身抛出来的错误(并非它的子组件)

  1. 仅处理渲染子组件期间的同步错误

  2. 错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。

属性默认值和类型检查

属性默认值

通过一个静态属性defaultProps告知react属性默认值。

  1. 函数组件 的属性默认值

在调用函数组件之前已经完成了混合。

import React from 'react'

// 函数组件的默认属性,
export function CompDefaultProps(props) {
    console.log(props)
    return <div>
        <h1>{props.name}</h1>
        <h2>{props.number}</h2>
    </div>
}

CompDefaultProps.defaultProps = {
    name: 'Drew',
    number: 24
}
  1. 类组件 的属性默认值。

在constructor 调用之前已经完成了混合。

  • 写法1
export class CompDefaultPropsCls extends React.Component {
    render() {
        return <div>
            <h1>{this.props.name}</h1>
            <h2>{this.props.number}</h2>
        </div>
    }
}

CompDefaultPropsCls.defaultProps = {
    name: "DrewCls",
    number: 24
}
  • 写法2
export class CompDefaultPropsCls extends React.Component {
    static defaultProps = {
        name: "DrewCls",
        number: 24
    }

    render() {
        return <div>
            <h1>{this.props.name}</h1>
            <h2>{this.props.number}</h2>
        </div>
    }
}

PropTypes 进行类型检查

  1. 官网 使用 PropTypes 进行类型检查 – React (reactjs.org)

  2. PropTypes

对组件使用静态属性propTypes告知react如何检查属性。

facebook/prop-types: Runtime type checking for React props and similar objects (github.com)

PropTypes.any//任意类型
PropTypes.array//数组类型
PropTypes.bool//布尔类型
PropTypes.func//函数类型
PropTypes.number//数字类型
PropTypes.object//对象类型
PropTypes.string//字符串类型
PropTypes.symbol//符号类型

PropTypes.node//任何可以被渲染的内容,字符串、数字、React元素
PropTypes.element//react元素
PropTypes.elementType//react元素类型
PropTypes.instanceOf(构造函数)://必须是指定构造函数的实例
PropTypes.oneOf([xxx, xxx])://枚举
PropTypes.oneOfType([xxx, xxx]);  //属性类型必须是数组中的其中一个
PropTypes.arrayOf(PropTypes.XXX)://必须是某一类型组成的数组
PropTypes.objectOf(PropTypes.XXX)://对象由某一类型的值组成
PropTypes.shape(对象): //属性必须是对象,并且满足指定的对象要求
PropTypes.exact({...})://对象必须精确匹配传递的数据

//自定义属性检查,如果有错误,返回错误对象即可
属性: function(props, propName, componentName) {
   //...
}
  1. 案例
  • 类组件使用PropTypes

image.png

react-note/1.prop-type 类型检查.html at master · PantherVkin/react-note (github.com)

  • 函数组件使用PropTypes

react-note/2.prop-type 类型检查-函数组件.html at master · PantherVkin/react-note (github.com)

HOC 高阶组件

HOF

Higher-Order Function, 高阶函数,以函数作为参数,并返回一个函数。

HOC

Higher-Order Component, 高阶组件,以组件作为参数,并返回一个组件

通常,可以利用HOC实现横切关注点。一般用来做功能增强的。

  1. 案例:withLog image.png

react-note/1.withLog.html at master · PantherVkin/react-note (github.com)

注意

  1. 不要在render/函数组件 中使用高阶组件,一开始就定义好

问题:

会导致每次都得到一个新的AComp、BComp,不断的在销毁组件。会浪费效率,会导致之前类组件的状态丢失。

img

  1. 不要在高阶组件内部更改传入的组件

  2. 命名习惯上with开头

Refs

reference: 引用。

ref 使用字符串

场景: 希望直接使用dom元素中的某个方法,或者希望直接只用自定义组件中的某个方法。

  1. ref作用于内置的html组件,得到的将是真实的dom对象

案例:点击按钮,input聚焦,ref使用字符串的方式。

      class Test extends React.Component {
        handleClick = () => {
          console.log(this.refs.inputRef, this.refs.inputRef.value);
          this.refs.inputRef.focus();
        };

        render() {
          return (
            <div>
              <input type="text" ref="inputRef" />
              <button onClick={this.handleClick}>点击聚焦</button>
            </div>
          );
        }
      }

image.png

react-note/1.ref使用字符串-内置的html组件.html at master · PantherVkin/react-note (github.com)

  1. ref 作用于类组件,得到的将是类的实例
      class A extends React.Component {
        method() {
          console.log('调用了组件A的方法');
        }

        render() {
          return <h1>组件A</h1>;
        }
      }

      class Comp extends React.Component {
        handleClick = () => {
          console.log(this);
          console.log(this.refs.txt.value);
          this.refs.compA.method();
        };

        render() {
          return (
            <div>
              <input ref="txt" type="text" />
              <A ref="compA" />
              <button onClick={this.handleClick}>聚焦</button>
            </div>
          );
        }
      }

image.png

react-note/2.ref使用字符串-类组件.html at master · PantherVkin/react-note (github.com)

  1. 注意:
  • ref 不再推荐使用字符串赋值,字符串赋值的方法将来可能会被移除

  • ref 不能作用于函数组件

  • ref 推荐使用对象或者是函数

ref 使用对象

通过React.createRef 函数创建。

      // ref 使用对象,通过React.createRef 函数创建
      class Test2 extends React.Component {
        constructor(props) {
          super(props);
          this.test2 = React.createRef();
        }

        handleClick = () => {
          console.log(this.test2);
          this.test2.current.focus();
        };

        render() {
          return (
            <div>
              <input type="text" ref={this.test2} />
              <button onClick={this.handleClick}>Test2点击聚焦</button>
            </div>
          );
        }
      }

img

react-note/3.ref使用对象.html at master · PantherVkin/react-note (github.com)

ref 使用函数

  1. 参数el

ref作用于内置的html组件,得到的将是真实的dom对象。

ref 作用于类组件,得到的将是类的实例。

      // ref 使用函数
      class Test3 extends React.Component {
        handleClick = () => {
          console.log(this.test3);
          this.test3.focus();
        };

        render() {
          return (
            <div>
              <input
                type="text"
                ref={(el) => {
                  this.test3 = el;
                }}
              />
              <button onClick={this.handleClick}>Test3点击聚焦</button>
            </div>
          );
        }
      }

react-note/4.ref使用函数.html at master · PantherVkin/react-note (github.com)

  1. ref 使用函数时 的调用时间
  • componentDidMount 的时候会调用该函数,在componentDidMount 中可以使用ref。

  • 如果ref的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在componentDidUpdate 之前

旧的函数被调用时,传递null;

新的函数被调用时,传递对象。

      // ref 使用函数
      // ref的值发生了变动(旧的函数被新的函数替代),分别调用旧的函数以及新的函数,时间点出现在componentDidUpdate 之前
      // 旧的函数被调用时,传递null
      // 新的函数被调用时,传递对象
      class Test4 extends React.Component {
        handleClick = () => {
          this.test4.focus();
          this.setState({});
        };

        render() {
          return (
            <div>
              <input
                type="text"
                ref={(el) => {
                  console.log('函数被调用了', el);
                  this.test4 = el;
                }}
              />
              <button onClick={this.handleClick}>Test4点击聚焦</button>
            </div>
          );
        }
      }

      function App() {
        return (
          <div className="App">
            <Test4></Test4>
          </div>
        );
      }

img

react-note/5.ref 使用函数时 的调用时间.html at master · PantherVkin/react-note (github.com)

  • 如果ref 所在的组件被卸载,会调用函数

谨慎使用ref

  1. 能够使用属性和状态进行控制,就不要使用ref。

  2. 使用场景:

  • 调用真实的dom对象中的方法

  • 某个时候需要调用类组件的方法

Ref 转发

需求?

ref 映射到自定义组件的内部。

函数组件

  1. React.forwardRef方法:
  • 参数:

传递的是函数组件,不能是类组件。

并且,函数组件需要有第二个参数来得到ref

  • 返回值:

返回一个新的组件。

      // 2. newA 把传递的ref  ,通过函数组件的第二个参数交给A处理。
      function A(props, ref) {
        return (
          <div>
            <h1 ref={ref}>Title: A</h1>
          </div>
        );
      }

      // 1. React.forwardRef 创建NewA
      // const NewA = React.forwardRef(function A(props, ref) {
      //   return (
      //     <div>
      //       <h1 ref={ref}>Title: A</h1>
      //     </div>
      //   );
      // });
      const NewA = React.forwardRef(A);

      class App extends React.Component {
        ARef = React.createRef();
        componentDidMount() {
          console.log('ref', this.ARef);
        }
        render() {
          return (
            <div className="App">
              {/* 2. newA 把传递的ref  ,通过函数组件的第二个参数交给A处理。 */}
              <NewA ref={this.ARef}></NewA>
            </div>
          );
        }
      }

img

react-note/1.React.forwardRef.html at master · PantherVkin/react-note (github.com)

类组件

  1. 通过props 把ref 传递过来
      // 类组件1: 通过props 把ref 传递过来。
      class A extends React.Component {
        render() {
          return (
            <div>
              <h1 ref={this.props.refA}>Title: A</h1>
            </div>
          );
        }
      }

      class App extends React.Component {
        ARef = React.createRef();

        componentDidMount() {
          console.log('ref', this.ARef);
        }
        render() {
          return (
            <div className="App">
              <A refA={this.ARef}></A>
            </div>
          );
        }
      }

img

react-note/2.类组件实现ref转发.html at master · PantherVkin/react-note (github.com)

  1. 使用forwardRef
      class A extends React.Component {
        render() {
          return (
            <div>
              <h1 ref={this.props.refA}>Title: A</h1>
            </div>
          );
        }
      }

      // 返回类组件,将ref 传递给refA 属性
      const NewA = React.forwardRef((prop, ref) => <A {...prop} refA={ref}></A>);
      class App extends React.Component {
        ARef = React.createRef();
        componentDidMount() {
          console.log('ref', this.ARef);
        }
        render() {
          return (
            <div className="App">
              <NewA ref={this.ARef}></NewA>
            </div>
          );
        }
      }

img

react-note/3.类组件使用forwardRef.html at master · PantherVkin/react-note (github.com)

常用场景

高阶组件中转发。

      /**
       * 高阶组件
       * @param {*} comp 组件
       */
      function withLog(Comp) {
        class LogWrapper extends React.Component {
          componentDidMount() {
            console.log(`日志:组件${Comp.name}被创建了!${Date.now()}`);
          }
          componentWillUnmount() {
            console.log(`日志:组件${Comp.name}被销毁了!${Date.now()}`);
          }
          render() {
            //正常的属性
            //forwardRef代表要转发的ref  {current:null}
            const {forwardRef, ...rest} = this.props;
            return (
              <div>
                <Comp ref={forwardRef} {...rest} />{' '}
              </div>
            );
          }
        }

        return React.forwardRef((props, ref) => {
          return <LogWrapper {...props} forwardRef={ref} />;
        });
      }

      class A extends React.Component {
        render() {
          return <h1>A:{this.props.a}</h1>;
        }
      }
      const AComp = withLog(A);
      class App extends React.Component {
        myRef = React.createRef();

        componentDidMount() {
          console.log(this.myRef);
        }
        render() {
          return (
            <div>
              <AComp ref={this.myRef} isLogin a={1} />{' '}
            </div>
          );
        }
      }

image.png

react-note/4.高阶组件中转发.html at master · PantherVkin/react-note (github.com)

PureComponent

PureComponent

  1. 纯组件,用于避免不可必要的渲染(运行render函数),从而提高效率。
  • 优化:如果一个组件的属性和状态,都没有发生变化,重新渲染该组件是没有必要的。

  • PureComponent是一个组件,如果某个组件继承自该组件,则该组件的sholdComponentUpdate 会进行优化,对属性和状态进行浅比较,如果相等则不会重新渲染

  1. PureComponent 进行是浅比较

为了效率,应尽量使用PureComponent;

要求不改动之前的状态,永远是创建新的状态覆盖之前的状态(Immutable ,不可变对象);

有一个第三方JS库,Immutable.js,他专门用于制作不可变对象。

import React, {PureComponent} from 'react'

class Test extends PureComponent{
    render() {
        return <h1>Test</h1>
    }
}

React.memo

  1. 函数组件使用React.memo 函数制作纯组件

React.memo 仅检查 props 变更。

如果函数组件被 React.memo 包裹,且其实现中拥有 useStateuseReduceruseContext 的 Hook,当 context 发生变化时,它仍会重新渲染。

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});
  1. 默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。
function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);

render props

有时候,某些组件的各种功能及其处理逻辑几乎完全相同,只是显示的界面不一样,建议下面的方式认选其一来解决重复代码的问题(横切关注点)。

  1. render props
  • 某个组件,需要某个属性

  • 该属性是一个函数,函数的返回值用于渲染

  • 函数的参数会传递为需要的数据

  • 注意:纯组件的属性(尽量避免每次传递的render props的地址不一致)

  • 通常该属性的名字叫做render

react-note/1.render props.html at master · PantherVkin/react-note (github.com)

  1. HOC

react-note/2.hoc.html at master · PantherVkin/react-note (github.com)

Portals

  1. 插槽

将一个React元素渲染到指定的DOM容器中

ReactDOM.createPortal(React元素, 真实的DOM容器),该函数返回一个React元素;

function Child() {
    return ReactDom.createPortal(<Test></Test>, document.querySelector('.modal'))
}

react-note/1.html at master · PantherVkin/react-note (github.com)

  1. 注意事件冒泡

React中的事件是包装过的;

它的事件冒泡是根据虚拟DOM树来冒泡的,与真实的DOM树无关

React中的事件

使用场景

需要和原生dom 事件混用的时候,需要注意。

这里的事件:React内置的DOM组件中的事件

  1. react给document注册事件
  • 几乎所有的元素的事件处理,均在document的事件中处理

  • 一些不冒泡的事件,是直接在元素上监听

  • 一些document上面没有的事件,直接在元素上监听

  1. 在document的事件处理,React会根据虚拟DOM树的完成事件函数的调用

  2. React的事件参数,并非真实的DOM事件参数,是React合成的一个对象,该对象类似于真实DOM的事件参数

  • stopPropagation阻止事件在虚拟DOM树中冒泡

  • nativeEvent,可以得到真实的DOM事件对象

  • 为了提高执行效率,React使用事件对象池来处理事件对象

注意事项

  1. 如果给真实的DOM注册事件,阻止了事件冒泡,则会导致react的相应事件无法触发

  2. 如果给真实的DOM注册事件,事件会先于React事件运行

  3. 通过React的事件中阻止事件冒泡,无法阻止真实的DOM事件冒泡

  4. 可以通过nativeEvent.stopImmediatePropagation()阻止document上剩余事件的执行

  5. 在事件处理程序中,不要异步的使用事件对象,如果一定要使用,需要调用persist函数

工具

严格模式

StrictMode(React.StrictMode),本质是一个组件,该组件不进行UI渲染(React.Fragment <> </>),它的作用是,在渲染内部组件时,发现不合适的代码。

  1. 识别不安全的生命周期

  2. 关于使用过时字符串 ref API 的警告

  3. 关于使用废弃的 findDOMNode 方法的警告

  4. 检测意外的副作用

React要求,副作用代码仅出现在以下生命周期函数中。

  • ComponentDidMount

  • ComponentDidUpdate

  • ComponentWillUnMount

副作用:一个函数中,做了一些会影响函数外部数据的事情,例如:

  • 异步处理

  • 改变参数值

  • setState

  • 本地存储

  • 改变函数外部的变量

相反的,如果一个函数没有副作用,则可以认为该函数是一个纯函数。

在严格模式下,虽然不能监控到具体的副作用代码,但它会将不能具有副作用的函数调用两遍,以便发现问题。(这种情况,仅在开发模式下有效)。

  1. 检测过时的 context API

Profiler

性能分析工具。

分析某一次或多次提交(更新),涉及到的组件的渲染时间。

  1. 火焰图:得到某一次提交,每个组件总的渲染时间以及自身的渲染时间。

  2. 排序图:得到某一次提交,每个组件自身渲染时间的排序。

  3. 组件图:某一个组件,在多次提交中,自身渲染花费的时间。