跳到主要内容

LifeCycle

前置知识 Render 阶段和 Commit 阶段

每个组件都包含生命周期,可以利用他们执行不同的逻辑。

生命周期图谱

执行顺序

由 JSX 渲染成真是 DOM 经历了Render阶段Commit 阶段。在这两个阶段里面分别执行组件的生命周期方法。

示例代码

let style = { border: '3px solid red', margin: '5px' };

let element = (
<div id='A1' style={style}>
A
<div id='B1' style={style}>
B1
</div>
<div id='B2' style={style}>
B2
</div>
</div>
);

let newElement = (
<div id='A1' style={style}>
TEXT_A
<div id='B1' style={style}>
TEXT_B1
</div>
<div id='B2-new' style={style}>
<p id='C'>CCC</p>
</div>
<div id='B3' style={style}>
TEXT_B3
</div>
</div>
);

class Son extends React.Component {
constructor() {
debugger;
console.log('Son === constructor');
super();
this.state = {
name: 'son',
};
}
static getDerivedStateFromProps(props, state) {
debugger;
console.log('Son === getDerivedStateFromProps', props, state);
return null;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
debugger;
console.log('Son === getSnapshotBeforeUpdate', prevProps, prevState);
return 'getSnapshotBeforeUpdate';
}
componentDidUpdate(prevProps, prevState, snapshot) {
debugger;
console.log('Son === componentDidUpdate', prevProps, prevState, snapshot);
}
componentDidMount() {
debugger;
console.log('Son === componentDidMount');
}
componentWillUnmount() {
debugger;
console.log('Son === componentWillUnmount');
}

render() {
let show = this.props.show;
return <>{show ? element : newElement}</>;
}
}

class Parent extends React.Component {
constructor() {
super();
this.state = {
name: 'diqiu',
show: true,
hidden: false,
};
debugger;
console.log('Parent === constructor');
}

handleClick = () => {
this.setState({
name: 'DIQIU-update',
show: false,
});
};
destroy = () => {
this.setState({
hidden: true,
});
};
static getDerivedStateFromProps(props, state) {
return null;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('Parent === getSnapshotBeforeUpdate', prevProps, prevState);
return 'getSnapshotBeforeUpdate';
}
componentDidMount() {
console.log('Parent === componentDidMount');
}
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps, nextState);
console.log('Parent === shouldComponentUpdate');
return true;
}
componentDidUpdate() {
console.log('Parent === componentDidUpdate');
}
componentWillUnmount() {
console.log('Parent === componentWillUnmount');
}
render() {
return (
<div id='app'>
<div id='name'>{this.state.name}</div>
<button onClick={this.handleClick}>更新</button>
<button onClick={this.destroy}>销毁</button>
{this.state.hidden ? '没啦' : <SonComp show={this.state.show} />}
</div>
);
}
}
ReactDOM.render(<Parent />, document.querySelector('#root'));

打印结果

// 首次渲染
Parent === constructor;
Parent === getDerivedStateFromProps;
Parent === render;
Son === constructor;
Son === getDerivedStateFromProps;
Son === render;
Son === componentDidMount;
Parent === componentDidMount;
// 更新子组件
Parent === getDerivedStateFromProps;
Parent === shouldComponentUpdate;
Parent === render;
Son === getDerivedStateFromProps;
Son === getSnapshotBeforeUpdate;
Son === shouldComponentUpdate;
Son === render;
Parent === getSnapshotBeforeUpdate;
Son === componentDidUpdate;
Parent === componentDidUpdate;
// 销毁子组件
Parent === getDerivedStateFromProps;
Parent === shouldComponentUpdate;
Parent === render;
Parent === getSnapshotBeforeUpdate;
Son === componentWillUnmount;
Parent === componentDidUpdate;

根据打印结果和源码咱们挨个 debug 分析

首次渲染

执行顺序如下:

Parent === constructor;
Parent === getDerivedStateFromProps;
Parent === render;
Son === constructor;
Son === getDerivedStateFromProps;
Son === render;
Son === componentDidMount;
Parent === componentDidMount;

在生成 fiber 节点时,根据 tag 的值去创建类组件示例触发相应方法

function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps: any,
renderLanes: Lanes
) {
constructClassInstance(workInProgress, Component, nextProps); // var instance = new ctor(props, context);
mountClassInstance(workInProgress, Component);
}
  1. 创建组件实例 instance 执行组件 construct 方法
  2. 对 workInProgress.memoizedState 赋值 instance.state
  3. 实例 instance.updater = classComponentUpdater;
  4. workInProgress.stateNode = instance
function constructClassInstance(
workInProgress: Fiber,
ctor: any,
props: any
): any {
// 创建组件实例 new component() 执行从组件内 construct方法
const instance = new ctor(props, context);
const state = (workInProgress.memoizedState =
instance.state !== null && instance.state !== undefined
? instance.state
: null);
// 实例instance挂载classComponentUpdater 后 讲实例挂载到workInProgress上
adoptClassInstance(workInProgress, instance);

// 返回实例
return instance;
}
  1. getDerivedStateFromProps 执行此方法并传入新 prop 和 state
  2. 执行 render 方法拿到 newChildren 继续执行
// 在以前从未渲染过的实例上调用挂载生命周期。
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes
): void {
const instance = workInProgress.stateNode;
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = emptyRefsObject;

initializeUpdateQueue(workInProgress);

const contextType = ctor.contextType;
if (typeof contextType === 'object' && contextType !== null) {
instance.context = readContext(contextType);
} else if (disableLegacyContext) {
instance.context = emptyContextObject;
} else {
const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
instance.context = getMaskedContext(workInProgress, unmaskedContext);
}

processUpdateQueue(workInProgress, newProps, instance, renderLanes);
instance.state = workInProgress.memoizedState;

const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// 调用 getDerivedStateFromProps 方法
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps
);
instance.state = workInProgress.memoizedState;
}

if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function'
) {
callComponentWillMount(workInProgress, instance);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
instance.state = workInProgress.memoizedState;
}

if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update;
}
}

完成 fiber 树收集 effectList 后,进入 commit 阶段

  1. 进入 commit 阶段的 commitLayoutEffects 后
  2. 调用 commitLifeCycles 判断组件第一次生成执行 componentDidMount
function commitLifeCycles(finishedRoot, current, finishedWork, committedLanes) {
switch (finishedWork.tag) {
case ClassComponent: {
var instance = finishedWork.stateNode;
if (finishedWork.flags & Update) {
if (current === null) {
{
instance.componentDidMount();
}
} else {
var prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
var prevState = current.memoizedState; // We could

{
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate
);
}
}
}
return;
}
}
}

更新组件

Parent === getDerivedStateFromProps;
Parent === shouldComponentUpdate;
Parent === render;
Son === getDerivedStateFromProps;
Son === getSnapshotBeforeUpdate;
Son === shouldComponentUpdate;
Son === render;
Parent === getSnapshotBeforeUpdate;
Son === componentDidUpdate;
Parent === componentDidUpdate;

当进行更新组件时,需要重新生成新的 fiber。通过 Render 阶段二次更新,fiber 会进行复用逻辑判断。因此相同的类组件不会再去创建实例。

  1. 进入 updateClassComponent 方法后,判断当前组件实例存在执行 updateClassInstance 方法。
function updateClassComponent(
current,
workInProgress,
Component,
nextProps,
renderLanes
) {
var instance = workInProgress.stateNode;
var shouldUpdate;

shouldUpdate = updateClassInstance(
current,
workInProgress,
Component,
nextProps,
renderLanes
);

var nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes
);
return nextUnitOfWork;
}
  1. 进入 updateClassInstance 方法后,触发 getDerivedStateFromProps 钩子函数,并在 checkShouldComponentUpdate 方法内触发 shouldComponentUpdate 钩子
function updateClassInstance(
current,
workInProgress,
ctor,
newProps,
renderLanes
) {
if (typeof getDerivedStateFromProps === 'function') {
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps
);
newState = workInProgress.memoizedState;
}

var shouldUpdate =
checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(
workInProgress,
ctor,
oldProps,
newProps,
oldState,
newState,
nextContext
);

instance.props = newProps;
instance.state = newState;
instance.context = nextContext;
return shouldUpdate;
}

完成更新的 fiber 树构建,收集 effectList 后进入 commit 阶段

  1. 首先进入 commitBeforeMutationLifeCycles,触发 getSnapshotBeforeUpdate 钩子
function commitBeforeMutationLifeCycles(current, finishedWork) {
switch (finishedWork.tag) {
case ClassComponent:
{
if (finishedWork.flags & Snapshot) {
if (current !== null) {
var prevProps = current.memoizedProps;
var prevState = current.memoizedState;
var instance = finishedWork.stateNode;

var snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps
: resolveDefaultProps(finishedWork.type, prevProps),
prevState
);

instance.__reactInternalSnapshotBeforeUpdate = snapshot;
}
}

return;
}
return;
}
}
  1. 进入 commitLayoutEffects 方法,判断是否复用老的 Fiber,去触发 componentDidUpdate 钩子
function commitLayoutEffects(root, committedLanes) {
while (nextEffect !== null) {
setCurrentFiber(nextEffect);
var flags = nextEffect.flags;

if (flags & (Update | Callback)) {
var current = nextEffect.alternate;
commitLifeCycles(root, current, nextEffect);
}

{
if (flags & Ref) {
commitAttachRef(nextEffect);
}
}

resetCurrentFiber();
nextEffect = nextEffect.nextEffect;
}
}

function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedLanes: Lanes
): void {
switch (finishedWork.tag) {
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.flags & Update) {
if (current === null) {
} else {
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
const prevState = current.memoizedState;
// We could update instance props and state here,
// but instead we rely on them being set during
if (
enableProfilerTimer &&
enableProfilerCommitHooks &&
finishedWork.mode & ProfileMode
) {
try {
startLayoutEffectTimer();
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate
);
} finally {
recordLayoutEffectDuration(finishedWork);
}
} else {
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate
);
}
}
}
}
}
}

销毁组件

Parent === getDerivedStateFromProps;
Parent === shouldComponentUpdate;
Parent === render;
Parent === getSnapshotBeforeUpdate;
Son === componentWillUnmount;
Parent === componentDidUpdate;

当销毁组件时新的 JSX 不存在子组件,在生成 fiber 树不会触发相应方法。

  1. 进入 commitMutationEffects 方法,判断其 flags 类型为删除,则执行 commitDeletion 方法
  2. commitUnmount 递归找到其子节点,触发子节点的 componentWillUnmount 钩子
function commitMutationEffects(root, renderPriorityLevel) {

while (nextEffect !== null) {

switch (primaryFlags) {
case Deletion: {
commitDeletion(root, nextEffect);
break;
}
}
}
function commitDeletion(finishedRoot, current, renderPriorityLevel) {
{
unmountHostComponents(finishedRoot, current);
}

var alternate = current.alternate;
detachFiberMutation(current);

if (alternate !== null) {
detachFiberMutation(alternate);
}
}

function unmountHostComponents(finishedRoot, current, renderPriorityLevel) {
commitUnmount(finishedRoot, node);
}