React筑基心法
JSX
babel编译
React17之前,需要显示导入React
import React from 'react' // 显示导入
<div key="propKey">Hello World</div>
/** babel编译结果 */
React.createElement('div', { key: 'propKey' }, "Hello", "World")
React17+,可以不显示导入React,但是需要配置tsconfig或babel的react预设
/** React17之后,不导入React */
<div key="propKey">Hello World</div>
/** babel编译结果 */
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime' // babel自动导入
_jsx('div', { key: 'keyProp', children: "Hello"})
_jsxs('div', { key: 'keyProp', children: ["Hello", "World"]})
babel和tsconfig配置二选一
/** babel和tsconfig配置二选一 */
/** babel配置 */
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-react', { runtime: 'automatic' }]
]
}
}, 'ts-loader']
}
/** tsconfig配置 */
{
...,
"jsx": "react-jsx" // 原配置"react"
}
type转换关系
jsx元素类型 | react.createElement 转换后 | type 属性 |
---|---|---|
element元素类型 | react element类型 | 标签字符串,例如 div |
fragment类型 | react element类型 | symbol react.fragment类型 |
文本类型 | 直接字符串 | 无 |
数组类型 | 返回数组结构,里面元素被 react.createElement转换 | 无 |
组件类型 | react element类型 | 组件类或者组件函数本身 |
三元运算 / 表达式 | 先执行三元运算,然后按照上述规则处理 | 看三元运算返回结果 |
函数执行 | 先执行函数,然后按照上述规则处理 | 看函数执行返回结果 |
常用API
React.Children.toArray (children)
将Children类数组转为常规数组类型,同时拍平数组包括深层次数组。
React.Children.forEach (children, (item, index) => {})
遍历Children类数组,且会先经过React.Children.toArray同样的处理。
React.Children.map () 同上,但是会返回一个新的Children
React.isValidElement (element) 判断是否是ReactElement
React.cloneElement (element, [newProps], [...newChildren])
拷贝返回一个新的元素,
props:newProps会覆盖与element.props中key相同的属性,element.props中没有的则会追加,newProps中没有的key则保留element.props的。
newChildren:newChildren会完全覆盖element.children
React组件
类组件构造
/** react/src/ReactBaseClasses.js **/
/**
* @note 类组件构造函数定义
* 当继承React.Component的组件在构造函数中执行super(props)时,调用的就是Component
*/
function Component(props, context, updater) {
this.props = props
this.context = context
this.refs = emptyObject
this.updater = updater || ReactNoopUpdateQueue
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function (partialState, callback) {
if (typeof partialState !== 'object' && typeof partialState !== 'function' && partialState != null)
throw new Error('takes an object of state variables to update or a ' + 'function which returns an object of state variables.',);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function (callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
/** react-reconciler/src/ReactFiberClassComponent.js */
/** @note 类组件渲染 FiberTag = 1 */
function constructClassInstance(
workInProgress: Fiber, // 当前工作中的Fiber
ctor: any, // 类组件构造函数
props: any, // 组件 props
): any {
/** @note 实例化组件,得到组件实例instance */
let instance = newctor(props, context);
}
函数组件构造
/** react-reconciler/src/ReactFiberHooks.js */
/** @note 函数组件渲染 FiberTag = 0 */
export function renderWithHooks<Props, SecondArg>(
current: Fiber | null, // 当前函数组件对应的Fiber,初始化
workInProgress: Fiber, // 当前工作中的Fiber
Component: (p: Props, arg: SecondArg) => any, // 函数组件
props: Props, // 函数组件的第一个参数props
secondArg: SecondArg, // 函数组件的其他参数,例如forwardRef中的ref,memo中的compare
nextRenderLanes: Lanes, // 下次渲染优先级(旧版是nextRenderExpirationTime)
): any {
/**
* @note 调用函数组件,得到React.element对象
*/
let children = __DEV__
? callComponentInDEV(Component, props, secondArg)
: Component(props, secondArg);
}
State
legacy模式
setState
this.setState(
(prevState, currentProps) => {},
() => {this.state /* 更新后的state */}
)
- 产生本次更新优先级lane(旧版是expirationTime)
- 从fiber root向下调和子节点,比对发生更新的地方,更新比对lane,找到发生更新的组件
- 合并state,执行render函数得到新的UI视图层
- commit替换真实DOM,完成更新
- 调用callback回调函数,结束setState
enqueueSetState
setState实际调用的就是updater.enqueueSetState
/** react-reconciler/src/ReactFiberClassComponent.js */
const classComponentUpdater = {
isMounted,
/** @note setState实际调用的是updater.enqueueSetState */
enqueueSetState(inst: any, payload: any, callback) {
const fiber = getInstance(inst);
const lane = requestUpdateLane(fiber); // 1.产生本次更新优先级(旧版是expirationTime)
const update = createUpdate(lane); // 创建update
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
warnOnInvalidCallback(callback);
}
update.callback = callback; // 设置更新回调函数
}
const root = enqueueUpdate(fiber, update, lane); // 将update加入Fiber
if (root !== null) {
startUpdateTimerByLane(lane); // 开始更新计时
scheduleUpdateOnFiber(root, fiber, lane); // 2.调度更新
entangleTransitions(root, fiber, lane);
}
// ...
}
// ...
}
批量更新
因为setState一般都是在用户交互时发生,所以setState的批量更新也是在React事件系统中统一调度的
/** react-dom-bindings/src/events/DOMPluginSystem.js */
/** @note 事件都将经过此函数同一处理 */
export function dispatchEventForPluginEventSystem(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
) {
// ...
/** @note 调用批量更新 */
batchedUpdates(() =>
dispatchEventsForPlugins(
domEventName,
eventSystemFlags,
nativeEvent,
ancestorInst,
targetContainer,
),
);
}
/** react-reconciler/src/events/ReactDOMUpdateBatching.js */
/** @note 事件批量更新 */
export function batchedUpdates(fn, a, b) {
if (isInsideEventHandler) {
// If we are currently inside another batch, we need to wait until it
// fully completes before restoring state.
return fn(a, b);
}
isInsideEventHandler = true; // 事件处理程序中,开启批量更新 旧版是isBatchingEventUpdates
try {
return batchedUpdatesImpl(fn, a, b); // 批量更新处理函数 ReactFiberWorkLoop.js -> batchedUpdates
} finally {
isInsideEventHandler = false;
finishEventHandler();
}
}
优先级
flushSync setState > 常规setState > setTimeout setState > Promise setState
常规setState
开启批量更新 -> setState加入队列 -> 合并state -> 更新state -> render -> callback -> 关闭批量更新
class Index extends React.Component {
state = initalState
onClick = () => {
// 1. 开启批量更新isInsideEvenetHandler=true,旧版是isBatchingEventUpdates
this.setState( // 2. 加入批量更新队列
newState1,
() => console.log(this.state) // 11. callback打印newState3
)
console.log(this.state) // 3. 打印initalState;
this.setState( // 4. 加入批量更新队列
newState2,
() => console.log(this.state) // 12. callback打印newState3
)
console.log(this.state) // 5. 打印initalState;
this.setState( // 6. 加入批量更新队列
newState3,
() => console.log(this.state) // 13. callback打印newState3
)
console.log(this.state) // 7. 打印initalState;
// 8. 合并批量更新:newState1 => newState2 => newState3
// 9. 更新为newState3,前往render
// 14. 关闭批量更新isInsideEvenetHandler=false
}
render() {
// 10. render,打印newState3
console.log(this.state)
return <div onClick={this.onClick} />
}
}
setTimeout setState
打破批量更新,多次setState多次render:
开启批量更新 -> setTimeout -> 关闭批量更新 -> 按加入队列的顺序执行setState -> 更新state -> render -> calllback -> 下一个加入队列的setState -> 循环到所有队列中setState结束
class Index extends React.Component {
state = initalState
onClick = () => {
// 1. 开启批量更新isInsideEvenetHandler=true,旧版是isBatchingEventUpdates
setTimeout(() => { // 2. 加入事件循环队列推迟执行
this.setState( // 4. 批量更新已关
newState1, // 5. 更新为newState1,前往render
() => console.log(this.state) // 7. callback打印newState1
)
console.log(this.state) // 8. 打印newState1
this.setState( // 9. 批量更新已关
newState2, // 10. 更新为newState2,前往render
() => console.log(this.state) // 12. callback打印newState2
)
console.log(this.state) // 13. 打印newState2
this.setState( // 14. 批量更新已关
newState3, // 15. 更新为newState3,前往render
() => console.log(this.state) // 17. callback打印newState3
)
console.log(this.state) // 18. 打印newState3
})
// 3. 关闭批量更新isInsideEvenetHandler=false
}
render() {
// 6. render,打印newState1
// 11. render,打印newState2
// 16. render,打印newState3
console.log(this.state)
return <div onClick={this.onClick} />
}
}
unstable_batchedUpdates
手动批量更新,强制合并setState,效果等同于常规的setState:
开启批量更新 -> setTimeout -> 关闭批量更新 -> unstable_batchedUpdates -> 常规setState
import ReactDOM from 'react-dom'
class Index extends React.Component {
state = initalState
onClick = () => {
// 1. 开启批量更新isInsideEvenetHandler=true,旧版是isBatchingEventUpdates
setTimeout(() => { // 2. 加入事件循环队列推迟执行
ReactDOM.unstable_batchedUpdates(() => { // 4. 强制开启批量更新
this.setState( // 5. 加入批量更新队列
newState1,
() => console.log(this.state) // 14. callback打印newState3
)
console.log(this.state) // 6. 打印initalState;
this.setState( // 7. 加入批量更新队列
newState2,
() => console.log(this.state) // 15. callback打印newState3
)
console.log(this.state) // 8. 打印initalState;
this.setState( // 9. 加入批量更新队列
newState3,
() => console.log(this.state) // 16. callback打印newState3
)
console.log(this.state) // 10. 打印initalState;
// 11. 合并批量更新:newState1 => newState2 => newState3
// 12. 更新为newState3,前往render
})
})
// 3. 关闭批量更新isInsideEvenetHandler=false
}
render() {
// 13. render,打印newState3
console.log(this.state)
return <div onClick={this.onClick} />
}
}
flushSync setState
高优先级批量更新:
开启批量更新 -> 上文setState加入队列 -> flushSync优先执行setState -> 合并当前队列state -> 更新state -> render -> callback -> 下文setState加入队列 -> 合并state -> 更新state -> render -> callback -> 关闭批量更新
import ReactDOM from 'react-dom'
class Index extends React.Component {
state = initalState
onClick = () => {
// 1. 开启批量更新isInsideEventHandler=true,旧版是isBatchingEventUpdates
setTimeout(() => { // 2. 加入事件循环队列推迟执行
this.setState( // 14. 批量更新已关
newState1, // 15. 更新为newState1,前往render
() => console.log(this.state) // 17. callback打印newState1
)
})
this.setState( // 3. 加入批量更新队列
newState2,
() => console.log(this.state)
)
ReactDOM.flushSync(() => { // 3. 高优先级执行
this.setState( // 4. 合并批量更新队列newState2 => newState3
newState3, // 5. 更新为newState3,前往render
() => console.log(this.state) // 7. callback打印newState3
)
})
this.setState( // 8. 加入批量更新队列
newState4,
() => console.log(this.state) // 13. callback打印newState4
)
// 9. 关闭批量更新isInsideEventHandler=false
// 10. 合并批量更新队列,newState4
// 11. 更新为newState4,前往render
}
render() {
// 6. render,打印newState3
// 12. render,打印newState4
// 16. render,打印newState1
console.log(this.state)
return <div onClick={this.onClick} />
}
}
useState
后续补充
concurrent模式
后续补充
生命周期
类组件
组件初始化(初次渲染)
fiber与实例之间互相获取:workInProgress.stateNode = 组件实例; instance._reactInternals = 实例fiber
初始化流程入口函数
// react-reconciler\src\ReactFiberBeginWork.js
/** @note 类组件更新主流程 */
function updateClassComponent(
current: Fiber | null, // 在第一次 fiber 调和之后,会将 workInProgress 树赋值给 current 树
workInProgress: Fiber, // 当前调和的fiber
Component: any, // 组件
nextProps: any, // 即将更新的props
renderLanes: Lanes, // 渲染优先级
) {
const props = [workInProgress,Component,nextProps,renderLanes] // 自己写的,节约行数
const instance = workInProgress.stateNode; // 从fiber中获取组件实例
let shouldUpdate; // 判断是否应该更新组件
if (instance === null) { // 实例不存在,说明是初次渲染
resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress);
// In the initial pass we might need to construct the instance.
constructClassInstance(workInProgress, Component, nextProps); // 构建组件实例 详情点击跳转
mountClassInstance(workInProgress, Component, nextProps, renderLanes); // 挂载组件实例
shouldUpdate = true; // 初始化挂载后需要更新
} else if (current === null) {
// In a resume, we'll already have an instance we can reuse.
// 有实例,但没有current,说明是恢复渲染,恢复实例挂载
shouldUpdate = resumeMountClassInstance(...props);
} else {
// 更新组件实例
shouldUpdate = updateClassInstance(current,...props);
}
// 前往执行render方法,获取组件的子节点,并进行深度调和
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderLanes,
);
return nextUnitOfWork; // 返回子节点fiber
}
constructor
constructor构建组件实例,前面介绍类组件构造中提到的constructClassInstance
挂载类组件实例函数
// react-reconciler\src\ReactFiberClassComponent.js
// Invokes the mount life-cycles on a previously never rendered instance.
/** @note 类组件挂载 */
function mountClassInstance(
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): void {
const instance = workInProgress.stateNode; // 类组件实例
instance.props = newProps;
instance.state = workInProgress.memoizedState;
instance.refs = {};
initializeUpdateQueue(workInProgress); // 初始化更新队列workInProgress.updateQueue
const contextType = ctor.contextType; // 获取组件构造函数上的静态属性contextType
// 下面是对组件实例的context属性进行处理赋值
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);
}
instance.state = workInProgress.memoizedState;
// 获取组件构造函数上的静态方法getDerivedStateFromProps
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// 调用getDerivedStateFromProps方法,将props和state合并得到新的state
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
instance.state = workInProgress.memoizedState;
}
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
// 如果组件有静态方法 getDerivedStateFromProps 或 实例上有 getSnapshotBeforeUpdate 则不会调用willMount
if (
typeof ctor.getDerivedStateFromProps !== 'function' &&
typeof instance.getSnapshotBeforeUpdate !== 'function' &&
(typeof instance.UNSAFE_componentWillMount === 'function' ||
typeof instance.componentWillMount === 'function')
) {
callComponentWillMount(workInProgress, instance); // 调用componentWillMount 和 UNSAFE_componentWillMount
// If we had additional state updates during this life-cycle, let's
// process them now.
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
suspendIfUpdateReadFromEntangledAsyncAction();
instance.state = workInProgress.memoizedState;
}
if (typeof instance.componentDidMount === 'function') {
workInProgress.flags |= Update | LayoutStatic; // 添加更新标记和布局标记
}
}
getDerivedStateFromProps
// react-reconciler\src\ReactFiberClassComponent.js
/** @note 从props衍生合并新的state */
function applyDerivedStateFromProps(
workInProgress: Fiber,
ctor: any,
getDerivedStateFromProps: (props: any, state: any) => any,
nextProps: any,
) {
const prevState = workInProgress.memoizedState;
let partialState = getDerivedStateFromProps(nextProps, prevState);
// Merge the partial state and the previous state.
const memoizedState =
partialState === null || partialState === undefined
? prevState
: assign({}, prevState, partialState);
workInProgress.memoizedState = memoizedState;
// Once the update queue is empty, persist the derived state onto the
// base state.
if (workInProgress.lanes === NoLanes) {
// Queue is always non-null for classes
const updateQueue: UpdateQueue<any> = (workInProgress.updateQueue: any);
updateQueue.baseState = memoizedState;
}
}
componentWillMount
// react-reconciler\src\ReactFiberClassComponent.js
/** @note 类组件挂载前调用componentWillMount 和 UNSAFE_componentWillMount */
function callComponentWillMount(workInProgress: Fiber, instance: any) {
const oldState = instance.state;
if (typeof instance.componentWillMount === 'function') {
instance.componentWillMount();
}
if (typeof instance.UNSAFE_componentWillMount === 'function') {
instance.UNSAFE_componentWillMount();
}
if (oldState !== instance.state) { // 如果state发生变化,进行替换state操作
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
}
enqueueReplaceState
:创建一个为state替换的更新加入调度队列
// react-reconciler\src\ReactFiberClassComponent.js
/** @note 类组件更新的updater对象 */
const classComponentUpdater = {
isMounted,
enqueueSetState() {...},
/** @note 创建一个为state替换的更新加入调度队列 */
enqueueReplaceState(inst: any, payload: any, callback: null) {
const fiber = getInstance(inst); // 获取实例对应的Fiber
const lane = requestUpdateLane(fiber); // 获取更新优先级
const update = createUpdate(lane); // 创建更新对象
update.tag = ReplaceState; // 设置更新标记为一次替换state的更新
update.payload = payload;
if (callback !== undefined && callback !== null) {
update.callback = callback; // 设置更新回调函数
}
const root = enqueueUpdate(fiber, update, lane); // 将更新加入Fiber的更新队列
if (root !== null) {
startUpdateTimerByLane(lane);
scheduleUpdateOnFiber(root, fiber, lane); // 调度更新
entangleTransitions(root, fiber, lane); // 处理并发模式的过渡更新
}
if (enableSchedulingProfiler) {
markStateUpdateScheduled(fiber, lane);
}
}
}
render
mountClassInstance至此执行完毕,回到updateClassComponent,调用finishClassComponent
获取children进行深度调和reconcileChildren
// react-reconciler\src\ReactFiberBeginWork.js
/** @note 类组件调和完成,执行render继续调和子组件 */
function finishClassComponent(省略,看上面的调用) {
const didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; // 是否捕获到错误
if (!shouldUpdate && !didCaptureError) { // 不需要更新,且没有捕获到错误
// Context providers should defer to sCU for rendering
if (hasContext) {
invalidateContextProvider(workInProgress, Component, false);
}
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
const instance = workInProgress.stateNode;
let nextChildren; // 子节点
nextChildren = instance.render(); // 执行render方法,获取子节点
reconcileChildren(current, workInProgress, nextChildren, renderLanes); // 深度调和子节点
return workInProgress.child; // 返回子节点fiber
}
componentDidMount
render调和完成之后,进入到commit阶段。
// react-reconciler\src\ReactFiberCommitWork.js
/** @note commit阶段,组件的layout生命周期处理 */
function commitLayoutEffectOnFiber(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedLanes: Lanes,
): void {
const prevEffectStart = pushComponentEffectStart(); // 开始记录组件的layout生命周期
// When updating this function, also update reappearLayoutEffects, which does
// most of the same things when an offscreen tree goes from hidden -> visible.
const flags = finishedWork.flags; // 当前fiber节点的flags
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
// 函数组件处理
case SimpleMemoComponent: {
// 递归子节点执行当前函数 commitLayoutEffectOnFiber
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Update) {
// 函数组件commit阶段的layout生命周期处理
commitHookLayoutEffects(finishedWork, HookLayout | HookHasEffect);
}
break;
}
// 类组件处理
caseClassComponent: {
// 递归子节点执行当前函数 commitLayoutEffectOnFiber
recursivelyTraverseLayoutEffects(
finishedRoot,
finishedWork,
committedLanes,
);
if (flags & Update) {
// 类组件commit阶段的layout生命周期处理
commitClassLayoutLifecycles(finishedWork, current);
}
if (flags & Callback) {
commitClassCallbacks(finishedWork);
}
if (flags & Ref) {
safelyAttachRef(finishedWork, finishedWork.return);
}
break;
}
case HostRoot: {略}
case HostHoistable: {略}
case HostSingleton:
case HostComponent: {略}
case Profiler: {略}
case SuspenseComponent: {略}
case OffscreenComponent: {略}
default: {略}
}
// 略
popComponentEffectStart(prevEffectStart); // 结束记录组件的layout生命周期
}
可以看到以下函数同时包含了**componentDidMount
和 componentDidUpdate
**的执行
// react-reconciler\src\ReactFiberCommitEffects.js
/** @note 类组件commit阶段的生命周期处理 */
export function commitClassLayoutLifecycles(
finishedWork: Fiber,
current: Fiber | null,
) {
const instance = finishedWork.stateNode; // 获取组件实例
if (current === null) {
// 初次挂载渲染阶段
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
if (__DEV__) {
// ...
}
if (shouldProfile(finishedWork)) {
// profile模式,开启性能检测
startEffectTimer();
// ... 省略代码同下方else代码块
recordEffectDuration(finishedWork);
} else {
if (__DEV__) {
// ...
} else {
try {
instance.componentDidMount(); // 调用componentDidMount生命周期
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
} else {
// 更新阶段
const prevProps = resolveClassComponentProps(
finishedWork.type,
current.memoizedProps,
finishedWork.elementType === finishedWork.type,
); // 获取上一次的props
const prevState = current.memoizedState; // 获取上一次的state
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
if (__DEV__) {
// ...
}
if (shouldProfile(finishedWork)) {
// profile模式,开启性能检测
startEffectTimer();
// ... 省略代码同下方else代码块
recordEffectDuration(finishedWork);
} else {
if (__DEV__) {
// ...
} else {
try {
// 调用componentDidUpdate生命周期
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
} catch (error) {
captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
}
}
总结
执行顺序:constructor -> getDerivedStateFromProps / componentWillMount -> render -> componentDidMount
组件更新
更新流程入口函数
和初始化流程同一个人口函数 updateClassComponent
更新类组件实例函数
// react-reconciler\src\ReactFiberClassComponent.js
/** @note 类组件更新阶段流程 */
function updateClassInstance(
current: Fiber,
workInProgress: Fiber,
ctor: any,
newProps: any,
renderLanes: Lanes,
): boolean {
const instance = workInProgress.stateNode; // 获取组件实例
cloneUpdateQueue(current, workInProgress);
// 下面是对新旧props的获取
const unresolvedOldProps = workInProgress.memoizedProps;
const oldProps = resolveClassComponentProps(
ctor,
unresolvedOldProps,
workInProgress.type === workInProgress.elementType,
);
instance.props = oldProps;
const unresolvedNewProps = workInProgress.pendingProps;
// 下面是对新旧context的获取
const oldContext = instance.context;
const contextType = ctor.contextType;
let nextContext = emptyContextObject;
if (typeof contextType === 'object' && contextType !== null) {
nextContext = readContext(contextType);
} else if (!disableLegacyContext) {
const nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
}
// 获取组件构造函数上的静态方法getDerivedStateFromProps
const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
// 判断是否有新的生命周期方法
const hasNewLifecycles =
typeof getDerivedStateFromProps === 'function' ||
typeof instance.getSnapshotBeforeUpdate === 'function';
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
typeof instance.componentWillReceiveProps === 'function')
) {
// 不存在新的生命周期方法
if (
unresolvedOldProps !== unresolvedNewProps ||
oldContext !== nextContext
) {
// 浅比较props和context,如果有变化,调用componentWillReceiveProps 和 UNSAFE_componentWillReceiveProps
callComponentWillReceiveProps(
workInProgress,
instance,
newProps,
nextContext,
);
}
}
resetHasForceUpdateBeforeProcessing();
// 获取新旧state
const oldState = workInProgress.memoizedState;
let newState = (instance.state = oldState);
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
suspendIfUpdateReadFromEntangledAsyncAction();
newState = workInProgress.memoizedState;
// props,state,context都无变化且没有强制更新
if (
unresolvedOldProps === unresolvedNewProps &&
oldState === newState &&
!hasContextChanged() &&
!checkHasForceUpdateAfterProcessing() &&
!(
enableLazyContextPropagation &&
current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies)
)
) {
// 不更新
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Snapshot;
}
}
return false;
}
if (typeof getDerivedStateFromProps === 'function') {
// 调用getDerivedStateFromProps方法,将props和state合并得到新的state
applyDerivedStateFromProps(
workInProgress,
ctor,
getDerivedStateFromProps,
newProps,
);
newState = workInProgress.memoizedState;
}
// 执行shouldComponentUpdate方法,判断是否需要更新
const shouldUpdate =
checkHasForceUpdateAfterProcessing() ||
checkShouldComponentUpdate(
workInProgress,
ctor,
oldProps,
newProps,
oldState,
newState,
nextContext,
) ||
// TODO: In some cases, we'll end up checking if context has changed twice,
// both before and after `shouldComponentUpdate` has been called. Not ideal,
// but I'm loath to refactor this function. This only happens for memoized
// components so it's not that common.
(enableLazyContextPropagation &&
current !== null &&
current.dependencies !== null &&
checkIfContextChanged(current.dependencies));
if (shouldUpdate) {
// In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for components using the new APIs.
if (
!hasNewLifecycles &&
(typeof instance.UNSAFE_componentWillUpdate === 'function' ||
typeof instance.componentWillUpdate === 'function')
) {
// 执行 UNSAFE_componentWillUpdate componentWillUpdate 方法
if (typeof instance.componentWillUpdate === 'function') {
instance.componentWillUpdate(newProps, newState, nextContext);
}
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
}
}
if (typeof instance.componentDidUpdate === 'function') {
workInProgress.flags |= Update;
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
workInProgress.flags |= Snapshot;
}
} else {
// If an update was already in progress, we should schedule an Update
// effect even though we're bailing out, so that cWU/cDU are called.
if (typeof instance.componentDidUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Update;
}
}
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
if (
unresolvedOldProps !== current.memoizedProps ||
oldState !== current.memoizedState
) {
workInProgress.flags |= Snapshot;
}
}
// If shouldComponentUpdate returned false, we should still update the
// memoized props/state to indicate that this work can be reused.
workInProgress.memoizedProps = newProps;
workInProgress.memoizedState = newState;
}
// Update the existing instance's state, props, and context pointers even
// if shouldComponentUpdate returns false.
instance.props = newProps;
instance.state = newState;
instance.context = nextContext;
return shouldUpdate;
}
组件销毁
函数组件
Fiber
Fiber Tag
export const FunctionComponent = 0; // 函数组件
export const ClassComponent = 1; // 类组件
export const IndeterminateComponent = 2; // 初始化的时候不知道是函数组件还是类组件
export const HostRoot = 3; // Root Fiber 可以理解为根元素 , 通过reactDom.render()产生的根元素
export const HostPortal = 4; // 对应 ReactDOM.createPortal 产生的 Portal
export const HostComponent = 5; // dom 元素 比如 <div>
export const HostText = 6; // 文本节点
export const Fragment = 7; // 对应 <React.Fragment>
export const Mode = 8; // 对应 <React.StrictMode>
export const ContextConsumer = 9; // 对应 <Context.Consumer>
export const ContextProvider = 10; // 对应 <Context.Provider>
export const ForwardRef = 11; // 对应 React.ForwardRef
export const Profiler = 12; // 对应 <Profiler/ >
export const SuspenseComponent = 13; // 对应 <Suspense>
export const MemoComponent = 14; // 对应 React.memo 返回的组件
Scheduler
Reconciler
事件合成
Concurrent
Suspense
React19
Hooks
effect
Ref
Context
Memo
Lazy Suspense
状态管理
Mobx
redux
React-router
React-router-dom
基于React-router上开发的适用Web环境的路由组件库,提供了常用的HashRouter,BrowserRouter,Link,NavLink,Routes(react-router6弃用Switch),Navigate(react-router4弃用Redirect)等组件
精准路由
动态路由
keep-alive
微前端路由
路由拦截
路由守卫
SSR
性能优化
React Scan
用普通函数还是箭头函数
/** 类组件 */
class Parent extends React.Component {
commonHanlder() {} // 创建在类组件上;普通函数的引用不会变化
arrowHanlder = () => {} // 创建在组件实例上;每次渲染会创建新的props对象,导致对箭头函数的引用会变化
render() {
return <>
<Child onClick={this.commonHanlder} /> // this指向变成了click事件而不是类组件
<Child onClick={this.arrowHanlder} /> // Parent每次渲染Child都跟着渲染
<Child onClick={() => {}} /> // 每次渲染重新定义新的函数
</>
}
}
/** 函数组件 每次渲染都是重新执行Parent函数 */
function Parent(props) {
function commonHandler() {} // 每次渲染重新定义新的函数 ps:普通函数由于js引擎的优化,创建与销毁的性能开销较小
const arrowHandler = () => {} // 每次渲染重新定义新的函数
return <>
<Child onClick={commonHanlder} /> // Parent每次渲染Child都跟着渲染
<Child onClick={arrowHanlder} /> // Parent每次渲染Child都跟着渲染
<Child onClick={() => {}} /> // 每次渲染重新定义新的函数
</>
}
最佳实践
类组件使用绑定this的普通函数;函数组件使用useCallback缓存函数;React.memo比较其他props来减少渲染;
/** 类组件 */
class Example extends React.Component {
constructor(props) {
super(props)
this.commonHanlder = this.commonHanlder.bind(this)
}
commonHanlder() {}
return <div onClick={this.commonHanlder} />
}
}
/** 函数组件 */
function Parent(props) {
const callbackHandler = useCallback(() => {}), []
return <Child onClick={callbackHandler} />
}