认识
2023年12月28日
一、认识
React.memo()
允许你的组件在 props
没有改变的情况下跳过重新渲染。如下所示:
默认比较函数: 默认情况下,React
将使用 Object.is
比较每个 prop
。
import { memo } from 'react';
function MyComponent(){
return <div>My Component</div>
}
const MemoizedComponent = memo(MyComponent)
自定义比较函数: 如果指定了自定的比较函数, React
将使用自定义比较函数比较每个 prop
。如果自定义比较函数返回 true
, 该组件将不会重新渲染。
import { memo } from 'react';
function MyComponent(){
return <div>My Component</div>
}
const MemoizedComponent = memo(MyComponent, (oldProps, newProps)=>{
return oldProps === newProps;
})
在 bailout
性能优化策略 中, 在 props
不变、state
不变、context
不变、type
不变的情况下, 会命中 bailout
策略, 会复用子组件, 子组件不会重新渲染。这时候传递给子组件的 props
引用地址是不变的。但是如果因为 props
或者 state
发生改变, 没有命中 bailout
策略, 会重新生成子组件, 传递给子组件的 props
也会是一个全新的地址。
基于以上原因, React.memo()
的作用是使 bailout
性能优化策略中的 props
的全等比较逻辑变为 props
浅比较。其本质是在子组件和父组件之间增加一个 MemoComponent
, MemoComponent
通过 props
的浅比较命中 bailout
策略, 使其 MemoComponent
的子组件不会重新渲染。
二、细节
-
定义
memo
函数 -
创建
fiber
时, 处理memo
类型fiber
-
beginWork
时, 处理memo
类型fiber
-
completeWork
时, 处理memo
类型fiber
三、定义 memo 函数
export function memo(type, compare) {
const fiberType = {
$$typeof: REACT_MEMO_TYPE,
type,
compare: compare === undefined ? null : compare
};
return fiberType;
}
四、创建 fiber 时, 处理 memo 类型 fiber
export function createFiberFromElement(element, lanes) {
const { ref, key, type, props } = element;
let fiberTag = FunctionComponent;
if (typeof type === 'string') {
fiberTag = HostComponent;
} else if (typeof type === 'object') {
switch (type.$$typeof) {
case REACT_PROVIDER_TYPE:
fiberTag = ContextProvider;
break;
case REACT_MEMO_TYPE:
fiberTag = MemoComponent;
break;
default:
break;
}
} else if (type === REACT_SUSPENSE_TYPE) {
fiberTag = SuspenseComponent;
} else if (typeof type !== 'function') {
console.log('未定义的 type 类型', element);
}
const fiber = new FiberNode(fiberTag, props, key);
fiber.ref = ref;
fiber.type = type;
fiber.lanes = lanes;
return fiber;
}
五、beginWork 时, 处理 memo 类型 fiber
beginWork
函数处理如下:
export const beginWork = (workInProgress, renderLane) => {
// bailout 策略
……
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
case HostRoot:
return updateHostRoot(workInProgress, renderLane);
case HostText:
return null;
case Fragment:
return updateFragment(workInProgress);
case HostComponent:
return updateHostComponent(workInProgress);
case FunctionComponent:
return updateFunctionComponent(
workInProgress,
workInProgress.type,
renderLane
);
case ContextProvider:
return updateContextProvider(workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(workInProgress);
case OffscreenComponent:
return updateOffscreenComponent(workInProgress);
case MemoComponent:
return updateMemoComponent(workInProgress, renderLane);
default:
console.log('beginWork 未实现的类型');
break;
}
return null;
};
updateMemoComponent
处理如下
function updateMemoComponent(workInProgress, renderLane) {
/**
* @description: 满足 bailout 四要素
* 1. props 浅比较
*/
const current = workInProgress.alternate;
const nextProps = workInProgress.pendingProps;
const Component = workInProgress.type.type;
if (current != null) {
const prevProps = current.memoizedProps;
if (
shallowEqual(prevProps, nextProps) &&
current.ref === workInProgress.ref
) {
didReceiveUpdate = false;
workInProgress.pendingProps = prevProps;
if (!checkScheduledUpdateOrContext(current, renderLane)) {
workInProgress.lanes = current.lanes;
return bailoutOnAlreadyFinishedWork(workInProgress, renderLane);
}
}
}
return updateFunctionComponent(workInProgress, Component, renderLane);
}
shallowEqual
浅比较逻辑如下:
export function shallowEqual(objA, objB) {
if (Object.is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!Object.hasOwnProperty.call(objB, currentKey) ||
!Object.is(objA[currentKey], objB[currentKey])
) {
return false;
}
}
return true;
}
六、completeWork 时, 处理 memo 类型 fiber
export const completeWork = workInProgress => {
const newProps = workInProgress.pendingProps;
const current = workInProgress.alternate;
switch (workInProgress.tag) {
……
case MemoComponent:
bubbleProperties(workInProgress);
return;
default:
console.log('completeWork 未实现的类型');
break;
}
};