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);
}
- 创建组件实例 instance 执行组件 construct 方法
- 对 workInProgress.memoizedState 赋值 instance.state
- 实例 instance.updater = classComponentUpdater;
- 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;
}
- getDerivedStateFromProps 执行此方法并传入新 prop 和 state
- 执行 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 阶段
- 进入 commit 阶段的 commitLayoutEffects 后
- 调用 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 会进行复用逻辑判断。因此相同的类组件不会再去创建实例。
- 进入 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;
}
- 进入 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 阶段
- 首先进入 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;
}
}
- 进入 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 树不会触发相应方法。
- 进入 commitMutationEffects 方法,判断其 flags 类型为删除,则执行 commitDeletion 方法
- 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);
}