React性能优化之SCU
原因
- 只要是修改了App中的数据,所有的组件都需要重新
render
,进行diff
算法,性能必然是很低的:- 事实上,很多的组件没有必须要重新
render
; - 它们调用
render
应该有一个前提,就是依赖的数据(state
、props
)发生改变时,再调用自己的render
方法;
- 事实上,很多的组件没有必须要重新
- 如何来控制render方法是否被调用呢?
- 通过
shouldComponentUpdate
方法即可;
- 通过
什么是SCU优化?
-
shouldComponentUpdate — SCU — React提供给我们的生命周期方法
- SCU优化就是 一种巧妙的技术,用来减少DOM操作次数,具体为当React元素没有更新时,不会去调用render()方法
- 可以通过
shouldComponentUpdate
来判断this.state
中的值是否改变
-
SCU 这个方法接受参数,并且需要有返回值:
- 该方法有两个参数:
- 参数一:nextProps 修改之后,最新的props属性
- 参数二:nextState 修改之后,最新的state属性
- 该方法返回值是一个boolean类型:
- 返回值为true,那么就需要调用render方法;
- 返回值为false,那么就不需要调用render方法;
- 默认返回的是true,也就是只要state发生改变,就会调用render方法;
- 该方法有两个参数:
shouldComponentUpdate(nextProps, nextState) {
// 从当前状态state中获取message和counter
const {message, counter} = this.state
// 如果message或counter与将要最新的状态nextState中不同,则返回true,表示需要更新组件。
if(message !== nextState.message || counter !== nextState.counter) {
return true
}
// 否则返回false,表示状态未发生变化,组件不需要更新。
return false
}
React已经帮我们提供好SCU优化的操作
类组件: 将class继承自 PureComponent
:
shallowEqual
方法
- 调用
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
,这个shallowEqual
就是进行浅层比较:
- 调用
//这个函数的目的是对比两个对象的浅层差异并返回布尔值,表示是否相等。如果两个对象具有相同的属性和值,则它们被认为是相等的。
function shallowEqual(objA, objB) {
// 如果两个对象完全相同,则返回true
if (Object.is(objA, objB)) {
return true;
}
// 如果objA和objB中有一个不是对象类型或为null,则返回false
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
// 获取objA和objB的所有属性名,并存储在数组keysA和keysB中
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
// 如果objA和objB的属性数量不相等,则返回false
if (keysA.length !== keysB.length) {
return false;
}
// 检查objA的每个属性是否都存在于objB中,
// 并且它们的值是否完全相等
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) || // objB中不存在该属性
!Object.is(objA[keysA[i]], objB[keysA[i]]) // 属性值不相等
) {
return false;
}
}
// 如果objA和objB的所有属性都相等,则返回true
return true;
}
函数组件: 使用一个高阶组件memo
-
React.memo()
是 React 中的一个高阶组件,用于优化函数式组件的性能。它能够在组件 props 没有变化时,避免不必要的重新渲染。 -
React.memo()
函数接收一个 React 组件作为参数,并返回一个包装后的组件。这个包装后的组件与原始组件具有相同的 API,但是会进行一些性能上的优化。 -
使用
React.memo()
包装的组件将对其 props 进行浅层比较(shallow comparison),只有当 props 发生变化时才进行重新渲染。因此,如果你知道组件的 props 变化频率不高,可以使用React.memo()
来减少组件不必要的重复渲染。
import {mome} from 'react'
const HomeFunc = mome(function(props) {
return (
<h4>函数式组件: {props.message}</h4>
)
})
export default HomeFunc
React 强调不可变的力量
不可变的力量: 不要直接去修改this.state中的值(主要指对象),若是想修改的话,应该是将这整个值全部修改掉。
注意: 值类型,在修改的时候,本身就全部替换掉了,所以不需要其他操作,直接改就可以
-
React 强调不可变的力量,是因为在 React 中使用不可变数据结构可以提高组件性能,并且使得代码更加健壮和易于维护。
-
使用不可变数据结构可以避免在直接修改数据时发生意外的副作用,例如无意中修改了
state
或props
对象,导致组件状态混乱或者重新渲染。同时,不可变数据结构还可以优化性能,因为它们能够让 React 更好地判断哪些部分的数据发生了变化,从而避免不必要的重新渲染。
-
React 中推荐的不可变数据结构主要包括以下几种:
- 数组:使用
concat()
、slice()
、map()
、filter()
等方法创建新数组。 - 对象:使用 ES2015 的扩展运算符(spread operator)或
Object.assign()
创建新对象。 - Map 和 Set:使用其内置的
set()
、delete()
、clear()
等方法。
- 数组:使用
-
总的来说,实现不可变的力量需要遵循以下原则:
-
尽可能使用 const 声明变量,确保不会意外地修改值。
-
不直接修改 state 或 props 中的值,而是创建新的对象或数组,从而避免副作用。
-
在传递数组或对象给子组件时,也要确保传递给子组件的是新的副本,而不是原始对象的引用。
-
合理地使用 JavaScript 提供的不可变数据结构,如数组、对象、Map 和 Set 等,可以更方便地支持不可变性,并提高代码的健壮性和可维护性。
-
下面是一些在 React 中实现不可变性的例子:
- 使用 concat() 方法创建新数组
function MyComponent(props) {
const [items, setItems] = useState([]);
function addItem(newItem) {
// 使用concat()方法来创建新数组,并将新元素添加到末尾
setItems(items.concat(newItem));
}
return (
<div>
{items.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
- 使用扩展运算符创建新对象
function MyComponent(props) {
const [user, setUser] = useState({ name: 'Alice', age: 30 });
function updateUser(newName) {
// 使用扩展运算符创建一个新的对象,并更新name属性
setUser({ ...user, name: newName });
}
return (
<div>
<div>Name: {user.name}</div>
<div>Age: {user.age}</div>
<button onClick={() => updateUser('Bob')}>Update Name</button>
</div>
);
}
- 使用 Map 数据结构创建新 Map
function MyComponent(props) {
const [myMap, setMyMap] = useState(new Map());
function addMapEntry(key, value) {
// 创建一个新Map对象,并使用set()方法添加新的键值对
const newMap = new Map(myMap);
newMap.set(key, value);
setMyMap(newMap);
}
return (
<div>
<ul>
{[...myMap.entries()].map(([key, value]) => (
<li key={key}>
{key}: {value}
</li>
))}
</ul>
<button onClick={() => addMapEntry('apple', 5)}>Add apple</button>
</div>
);
}
通过使用以上方式,我们可以避免直接修改数据并创建新的不可变数据结构。这些方法不仅能提高 React 组件的性能,同时也帮助我们写出更健壮和可维护的代码。
原文链接:https://juejin.cn/post/7225141192605548599 作者:星_墨