React 基础巩固(二十三)——React 性能优化 SCU
React 更新机制
-
React 的渲染流程
- JSX -> 虚拟 DOM -> 真实 DOM
-
React 的更新流程
props/state 改变
->render函数重新执行
->产生新的DOM树
->新旧DOM树进行diff
->计算出差异进行更新
->更新到真实的DOM
-
React 在 props 或 state 发生改变时,会调用 React 的 render 方法,会创建一颗不同的树
-
React 需要基于这两颗不同的树之间的差别来判断如何有效的更新 UI
- 如果一颗树参考另外一颗树进行完全比较更新,那么即使时最先进的算法,该算法的复杂度为 O(n^2),其中 n 是树中元素的数量;
- 如果在 React 中使用了该算法,那么展示 1000 个元素所需要执行的计算量将在 10 亿的量级范围;
- 开销过于昂贵,更新性能非常低效;
-
于是,React 对这个算法进行了优化,将其优化成了 O(n)
- 同层节点之间相互比较,不会跨节点比较
- 不同类型的节点,产生不同的树结构
- 开发中,可以通过 key 来指定哪些节点在不同的渲染下保持稳定
-
keys 的优化
- 遍历列表时,会提示一个警告,提示我们加入一个 key 属性
- 当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素
- 例如将 key 为 333 的元素插入到最前面的位置时,key 为 111,222 的元素,仅仅需要进行位移即可,不需要任何修改
-
key 的注意事项
- key 应该是唯一的
- key 不要使用随机数(随机数在下一次 render 时,会重新生成一个数字)
- 使用 index 作为 key,对性能是没有优化的
-
render 函数被调用
- 为了避免不必要的 render,提高性能,调用 render 应该有一个前提:依赖的数据(state、props)发生改变时,再调用 render 方法
-
利用
shouldComponentUpdate
优化 render 调用
// 针对值发生变化的组件
shouldComponentUpdate(nextProps, newState){// 对比 state中的值是否有改变if (this.state.message !== newState.message || this.state.counter !== newState.counter){return true}return false
}// 针对其中的子组件
shouldComponentUpdate(nextProps, newState){// 1.对比 state中的值是否有改变if (this.state.message !== newState.message || this.state.counter !== newState.counter){return true}// 2.检测接收的props是否有改变if (this.props.message !== newProps.message){return true}return false
}
- 如果参数过多,SCU 的方式则过于繁琐,所以通常采用以下方式实现
针对类组件:采用PureComponent 实现优化
import React, { PureComponent } from "react";
export class App extends PureComponent {// ...
}
PureComponent中变更数组数据的实际应用:
import React, { PureComponent } from "react";export class App extends PureComponent {constructor() {super();this.state = {books: [{ name: "book1", price: 199, count: 1 },{ name: "book2", price: 299, count: 1 },{ name: "book3", price: 399, count: 2 },{ name: "book4", price: 499, count: 3 },{ name: "book5", price: 599, count: 1 },],};}addNewBook() {const newBook = {name: "book6",price: 55,count: 1,};// 错误写法:// 直接修改原有的state,重新设置一遍// this.state.books.push(newBook);// this.setState({// books: this.state.books,// });// 正确写法:// 拷贝一份,在新的数组中更新,设置新的books,而不是直接修改stateconst books = [...this.state.books];books.push(newBook);this.setState({ books });}addBookCount(index) {console.log(index);const books = [...this.state.books];books[index].count++;this.setState({ books });}render() {const { books } = this.state;return (<div><h2>books list</h2><ul>{books.map((item, index) => {return (<li key={index}><span>name: {item.name} - price: {item.price} - count: {item.count}</span><button onClick={(e) => this.addBookCount(index)}>+1</button></li>);})}</ul><button onClick={(e) => this.addNewBook()}>添加新书籍</button></div>);}
}export default App;
针对函数式组件:采用 memo 实现优化
import { memo } from "react";
const Profile = memo(function (props) {console.log("profile render");return <h2>Profile: {props.message}</h2>;
});export default Profile;