export const ComponentA = props => {
console.log('A render')
const [count, setCount] = useState(0)
// ref 回调,避免只拿到渲染前的null
const commentListRefCB = useMemoizedFn(domRef => {
console.log('commentListRefCBcommentListRefCBcommentListRefCB')
if (domRef) {
console.log('commentListRefCBcommentListRefCBcommentListRefCB进入 nnode不为空')
}
})
useEffect(() => {
console.log('进入A useEffect')
setCount(2)
}, [])
useLayoutEffect(() => {
console.log('进入A useLayoutEffect')
setCount(1)
})
return(
<div ref={commentListRefCB}>
<span> {count} </span>
<ComponentB>
</div>
)
}
export const ComponentB = props => {
useEffect(() => {
console.log('进入B useEffect')
}, [])
useLayoutEffect(() => {
console.log('进入B useLayoutEffect')
})
return(
<div>
BBB
</div>
)
}
1. 渲染(Render)阶段
-
定义:渲染是指React根据组件的状态(
state)和属性(props)生成虚拟DOM(Virtual DOM)的过程。 -
特点:
- 渲染是纯函数操作,不会直接操作真实的DOM。
- 渲染阶段可能会被React暂停、中断或重新开始(尤其是在并发模式下)。
- 在渲染阶段,React会调用组件的
render方法(或函数组件的返回值)来生成虚拟DOM。
-
触发时机:
- 组件首次挂载时。
- 组件的
state或props发生变化时。 - 父组件重新渲染时(除非使用
React.memo或shouldComponentUpdate进行优化)。
2. 挂载(Mount)阶段
-
定义:挂载是指将渲染生成的虚拟DOM转换为真实DOM,并将其插入到浏览器的DOM树中的过程。
-
特点:
- 挂载阶段会操作真实的DOM。
- 在挂载阶段,React会调用生命周期方法或Hooks(如
useLayoutEffect和useEffect)。 - 挂载阶段是同步的,React会确保在浏览器绘制之前完成挂载。
-
触发时机:
- 组件首次渲染后。
- 组件从DOM中移除后再次插入时(例如通过条件渲染)。
3. 渲染和挂载的关系
-
渲染在前,挂载在后:
- React会先进行渲染,生成虚拟DOM。
- 然后根据虚拟DOM的变化,进行挂载或更新操作。
-
挂载是渲染的结果:
- 渲染阶段决定了虚拟DOM的结构。
- 挂载阶段将虚拟DOM转换为真实DOM,并将其插入到页面中。
4. 代码中的渲染和挂载顺序
以下是你提供的代码中渲染和挂载的具体顺序:
4.1 渲染阶段
-
ComponentA渲染: -
- 打印
ComponentA render。 - 生成
ComponentA的虚拟DOM。 - 发现
ComponentB是ComponentA的子组件,开始渲染ComponentB。
- 打印
-
ComponentB渲染:- 生成
ComponentB的虚拟DOM。
- 生成
4.2 挂载阶段
-
ComponentA的ref回调:- 在
ComponentA的div元素挂载到DOM时,调用commentListRefCB。 - 打印
commentListRefCBcommentListRefCBcommentListRefCB。 - 如果
domRef不为null,打印commentListRefCBcommentListRefCBcommentListRefCB进入 nnode不为空。
- 在
-
useLayoutEffect的执行:(React会先执行子组件的useLayoutEffect,再执行父组件的useLayoutEffect)- React会先执行子组件的
useLayoutEffect,再执行父组件的useLayoutEffect。 - 打印
进入B useLayoutEffect(ComponentB的useLayoutEffect)。 - 打印
进入A useLayoutEffect(ComponentA的useLayoutEffect)。
- React会先执行子组件的
-
浏览器绘制:
- 浏览器将更新后的DOM绘制到屏幕上。
-
useEffect的执行:(React会先执行子组件的useEffect,再执行父组件的useEffect)- React会先执行子组件的
useEffect,再执行父组件的useEffect。 - 打印
进入B useEffect(ComponentB的useEffect)。 - 打印
进入A useEffect(ComponentA的useEffect)。
- React会先执行子组件的