# 1.React生命周期

  • Initialization:初始化阶段。
  • Mounting:挂载阶段。
  • Updation:更新阶段。
  • Unmounting:销毁阶段

# Mounting 阶段

Mounting阶段叫挂载阶段,伴随着整个虚拟DOM的生成,它里边有三个小的生命周期函数,分别是:

  • componentWillMount : 在组件即将被挂载到页面的时刻执行。
  • render : 页面state或props发生变化时执行。
  • componentDidMount : 组件挂载完成时被执行。

# Updation 阶段

组件发生改变的更新阶段,这是React生命周期中比较复杂的一部分 它有两个基本部分组成,一个是props属性改变,一个是state状态改变

  • shouldComponentUpdate函数 (组件更新之前)

要求必须有返回值,返回一个布尔类型的结果。 简单点说,就是返回true,就同意组件更新;返回false,就反对组件更新。

  • componentWillUpdate函数(组件更新之后)

componentWillUpdate在组件更新之前,但shouldComponenUpdate之后被执行。但是如果shouldComponentUpdate返回false,这个函数就不会被执行了。

  • componentWillReceiveProps函数

子组件接收到父组件传递过来的参数,父组件render函数重新被执行,这个生命周期就会被执行。 也就是说这个组件第一次存在于Dom中,函数是不会被执行的; 如果已经存在于Dom中,函数才会被执行。

# Unmounting 阶段

  • componentWillUnmount函数

组件从页面中删除的时候执行

# React组件的生命周期可以大致分为三个阶段:实例化期存在期销毁期

在不同的生命时期对应有钩子函数

# 1.1 实例化期

  • 1、getDefaultProps: 设置默认的props(此方法只适合React.createClass创建的组件,ES6方式使用 static defaultProps={}的形式
  • 2、getInitialState:设置默认的状态state。ES6方式对应在contructor中直接设置this.state
  • 3、componentWillMount:该方法会在组件首次渲染之前调用,这是在render方法调用前可修改state的最后一次机会
  • 4、render:渲染。
  • 5、componentDidMount:在首次真实DOM渲染后调用,当需要访问真实的DOM时,常用,请求外部接口数据一般在这里。(服务端中,该方法不会被调用)

# 1.2 存在期

在实例化后,当props或state发生变化时,下面方法依次被调用:

  • 1、componentWillReceiveProps:当通过父组件更新子组件props时,这个方法就会被调用
  • 2、shouldComponentUpdate(nextProps,nextState):是否该更新组件,默认返回true,当返回false时,后期函数就不会调用,组件不会再次渲染,用来优化性能
  • 3、componentWillUpdate:组件即将更新,props和state改变后必调用
  • 4、render
  • 5、componentDidUpdate:更新真实DOM成功后调用,这时可以访问真实的DOM

# 1.3 销毁期

componentWillUnmount:组件被移除之前被调用,可以用于做一些清理工作,在componentDidMount方法中添加的所有任务都需要在该方法中撤销,比如创建的定时器或添加的事件监听器。当我们在组件中使用了 setInterval,那我们就需要在这个方法中调用clearTimeout。

# 2.React Hooks

React Hooks 的设计目的,就是加强版函数组件,完全不使用"类",就能写出一个全功能的组件。

React 默认提供的四个最常用的钩子。

  • useState() 状态钩子
  • useContext() 父子组件传值
  • useReducer()
  • useEffect() 生命周期

class 组件和 Hooks 对比

import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { useState } from "react";

export default function  Button()  {
  const  [buttonText, setButtonText] =  useState("Click me,   please");

  function handleClick()  {
    return setButtonText("Thanks, been clicked!");
  }

  return  <button  onClick={handleClick}>{buttonText}</button>;
}

useState()
// 第一个成员是一个变量
// 第二个成员是一个函数,用来更新状态,约定是set前缀加上状态的变量名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2.1 useEffect详解

# 2.1.1 不传递

useEffect不传递第二个参数会导致每次渲染都会运行useEffect。

然后,当它运行时,它获取数据并更新状态。然后,一旦状态更新,组件将重新呈现,这将再次触发useEffect,这就是问题所在。

useEffect(()=>{
    console.log(props.number)
    setNumber(props.number)
}) //所有更新都执行
1
2
3
4

# 2.1.2 传递空数组

useEffect(()=>{
    console.log(props)
},[]) //仅在挂载和卸载的时候执行
1
2
3

# 2.1.3 传递一个值

useEffect(()=>{
    console.log(count)
},[count]) //count更新时执行
1
2
3

# 2.1.4 传递多个

const Asynchronous : React.FC=({number})=>{
const [number2,setNumber2] = useState(number);
useEffect(()=>{
    console.log(number)
    setNumber2(number)
},[number,setNumber2]) //监听props对象number的更改
//setNumber2是useState返回的setter,所以不会在每次渲染时重新创建它,因此effect只会运行一次
}
1
2
3
4
5
6
7
8

# 3.Context

官方定义:在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但此种用法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

Context提供了一个局部的全局作用域,使用Context则无需再手动的逐层传递props。

3种Context的使用方式:

  • React.createContext提供的ProviderConsumer
  • 函数组件:React.createContext提供的ProvideruseContext钩子
  • Class组件:React.createContext提供的Provider和class的contextType属性

先定义好createContext

// Context.jsx
import { createContext } from "react";

export default createContext();
1
2
3
4

# 3-1. React.createContext提供的Provider和Consumer

// App.js
import React, { createContext } from "react";
import MyContext from "./Context";

import FirstTest from "./FirstTest";

export default function App() {
  return (
    //Provider组件接收一个value属性,此处传入一个带有name属性的对象
    <MyContext.Provider value={{ name: `test` }}>
      <FirstTest />
    </MyContext.Provider>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// FirstTest.jsx
import React from "react";
import MyContext from "./Context";

const FirstTest = () => {
  return (
    <MyContext.Consumer>
      {(value) => {
        return (
          <div>{JSON.stringify(value)}</div> // test
        );
      }}
    </MyContext.Consumer>
  );
};

export default FirstTest;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3-2. 函数组件:React.createContext提供的Provider和useContext钩子

// SecondTest.jsx
import React, { useContext } from "react";
import MyContext from "./Context";

const SecondTest = () => {
  const context = useContext(MyContext);
  return <div>{JSON.stringify(value)}</div>; // test
};

export default SecondTest;
1
2
3
4
5
6
7
8
9
10

# 3-3. Class组件:React.createContext提供的Provider和class的contextType属性

// ThirdTest.jsx
import React, { Component } from "react";
import context from "./Context";

class ThirdTest extends Component {
  static contextType = context;
  render() {
    const value = this.context;
    return <div>{JSON.stringify(value)}</div>; // test
  }
}

// ThirdTest.contextType = context; //此处与写static关键字作用一致
export default ThirdTest;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.useMemo

useMemo是 React 提供的一个 hook 函数。允许开发人员缓存变量的值和依赖列表。如果此依赖项列表中的任何变量发生更改,React 将重新运行此函数去处理并重新缓存它。如果依赖项列表中的变量值没有改版,则 React 将从缓存中获取值。

基本可以等价于Vue 的 computed

React 官方文档介绍

TIP

You may rely on useMemo as a performance optimization 您可以依赖 useMemo 作为性能优化工具

  • 基本用法
import React, { useMemo, useState } from "react";

export default function App() {
  const [myNum, setMyNum] = useState(0);
  const [otherNum, setOtherNum] = useState(0);

  const newVal = useMemo(() => {
    return myNum + otherNum;
  }, [myNum, otherNum]);

  const addOneNum = () => {
    let val = myNum + 1;
    setMyNum(val);
  };

  const addTwoNum = () => {
    let val = otherNum + 2;
    setOtherNum(val);
  };

  return (
    <div className="App">
      result:{newVal}
      <button onClick={addOneNum}>addOneNum</button>
      <button onClick={addTwoNum}>addTwoNum</button>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

useMemo第一个参数是函数,第二个参数是数组,数组里面的元素是依赖项

# 5.React diff算法

React 的 Diff 算法基于以下 三大策略 优化性能:

同级比较(Tree Diff)组件类型一致性key 属性优化

  • 1.同级比较(Tree Diff)

    • 仅对比 同一层级 的节点,不跨层级比较(复杂度从 O(n³) 降至 O(n))。
    • 如果节点类型不同(如 divspan),直接销毁并重建整个子树。
  • 2.组件类型一致性

    • 相同类型的组件(如 <Button />)会复用实例,触发更新逻辑(如 shouldComponentUpdate)。
    • 不同类型则销毁旧组件,挂载新组件。
  • 3.key 属性优化

    • 对列表节点,key 帮助 React 识别节点的唯一性,减少不必要的销毁/重建。

React的diff 算法采用了 深度优先遍历算法

  • react 采用单指针从左向右进行遍历
  • vue采用双指针,从两头向中间进行遍历

React 不能通过双端对比进行 Diff 算法优化是因为目前 Fiber 上没有设置反向链表。

# 6.class组件对应的hook替代方案

# Class 组件原版

class Example extends React.Component {
  state = { count: 0 };
  
  componentDidMount() {
    console.log('Mounted');
  }
  
  componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
      console.log('Count updated:', this.state.count);
    }
  }
  
  componentWillUnmount() {
    console.log('Unmounting');
  }
  
  render() {
    return (
      <button onClick={() => this.setState({ count: this.state.count + 1 })}>
        Clicked {this.state.count} times
      </button>
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 函数组件 Hook 版

function Example() {
  const [count, setCount] = useState(0); // 等价于state
  
  useEffect(() => {
    console.log('Mounted'); // 等价于componentDidMount
    return () => console.log('Unmounting');  // 等价于componentWillUnmount
  }, []);  
  
  useEffect(() => { // 等价于componentDidUpdate
    console.log('Count updated:', count);
  }, [count]);
  
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Clicked {count} times
    </button>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lastUpdate: 5/4/2025, 12:01:19 PM