# 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>;
}
}
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前缀加上状态的变量名
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)
}) //所有更新都执行
2
3
4
# 2.1.2 传递空数组
useEffect(()=>{
console.log(props)
},[]) //仅在挂载和卸载的时候执行
2
3
# 2.1.3 传递一个值
useEffect(()=>{
console.log(count)
},[count]) //count更新时执行
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只会运行一次
}
2
3
4
5
6
7
8
# 3.Context
官方定义:在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但此种用法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
Context提供了一个局部的全局作用域,使用Context则无需再手动的逐层传递props。
3种Context的使用方式:
React.createContext
提供的Provider
和
Consumer- 函数组件:
React.createContext
提供的Provider
和useContext
钩子 - Class组件:
React.createContext
提供的Provider
和class的contextType
属性
先定义好createContext
// Context.jsx
import { createContext } from "react";
export default createContext();
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>
);
}
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;
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;
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;
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>
);
}
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))。
- 如果节点类型不同(如
div
→span
),直接销毁并重建整个子树。
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>
);
}
}
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>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
← React基础 React18新特性 →