最新React的uEffect与uLayoutEffect执行机制剖析

更新时间:2023-06-22 21:26:04 阅读: 评论:0

最新React的uEffect与uLayoutEffect执⾏机制剖析
引⾔
uEffect和uLayoutEffect是React官⽅推出的两个hooks,都是⽤来执⾏副作⽤的钩⼦函数,名字类似,功能相近,唯⼀不同的就是执⾏的时机有差异,今天这篇⽂章主要是从这两个钩⼦函数的执⾏时机⼊⼿,来剖析⼀下React的运⾏原理和浏览器的渲染流程。
官⽅解释
uLayoutEffect其函数签名与 uEffect 相同,但它会在所有的 DOM 变更之后同步调⽤ effect。可以使⽤它来读取 DOM 布局并同步触发重渲染。在浏览器执⾏绘制之前, uLayoutEffect 内部的更新计划将被同步刷新,尽可能使⽤标准的 uEffect 以避免阻塞视觉更新。
简单来讲,就是:uEffect是异步的,uLayoutEffect是同步的,异(同)步是相对于浏览器执⾏刷新屏幕Task来说的。
眼见为实
下⾯将通过⼀个简单的demo⽰例来说明具体的执⾏过程,其中React是16.13.1版本,⾸先是⽰例代码:
import React, { uState, uEffect, uLayoutEffect } from ‘react’;
const EffectDemo = () => {
const [count, tCount] = uState(0);
uEffect(function uEffectDemo() {
console.log(‘uEffect:’, count);
}, [count]);
uLayoutEffect(function uLayoutEffectDemo() {
console.log(‘uLayoutEffect:’, count);
}, [count]);
return (
<div>
<button
onClick={() => {
tCount(count + 1);
}}
>click me</button>
</div>
);托运行李英文
};
export default EffectDemo;
功能很简单,就不做界⾯展⽰,这⾥主要是看⼀下浏览器控制台Performance的监控图:
图⽚描述
通过两个hooks的执⾏图可以看出,uLayoutEffect发⽣在页⾯渲染到屏幕(⽤户可见)之前,uEffect发⽣在那之后,中间还经历了DCL,FCP,FMP,LCP阶段,除开DCL(DomContentLoaded)之外,这些指标是RAIL模型衡量页⾯性能的标准,总的来说,渲染到屏幕的阶段是⼀个分⽔岭,那么渲染包含什么呢,还是看图吧:
图⽚描述
此阶段完成了样式的计算(Recalculate Style)和布局(Layout),紧接着是⼀个Task,完成Update Layer Tree,Paint,Composite Layers,经过这⼀系列的任务后,页⾯最终呈现给⽤户,可以⽤⼀张图来表⽰浏览器的渲染过程:
图⽚描述
后⾯会有相关学习资料,这⾥就不展开细说了。
模拟运⾏⽰例
在深⼊了解React的运⾏之前,⾸先在本地写⼀个简单的⽰例,⼤致模拟⽂章开始的例⼦:
然后启⽤Performance监控渲染情况:
图⽚描述
总结⼀下:
1.⾸先运⾏render,完成后⽴即执⾏uLayoutEffectDemo函数(虽然已经插⼊DOM,但是界⾯还没有渲染出来);
2.注册异步回调函数uEffectDemo,该函数将在0ms过后加⼊EventLoop中的宏任务队列;
3.页⾯开始渲染:Recalculate Style->Layout->Update Layer Tree->Paint->Composite Layers->GPU绘制;
4.取出宏任务uEffectDemo,执⾏回调;
React的执⾏⽐这个模拟⽰例复杂很多,但是抽象出的流程节点⼤同⼩异,了解之后,我们可以继续深⼊挖掘React的运⾏机制了。
React运⾏原理
React渲染页⾯分为两个阶段:
1.调度阶段(reconciliation):找出需要更新的节点元素
2.渲染阶段(commit):将需要更新的元素插⼊DOM
接下来就跟着React的运⾏流程来具体看下不同阶段的执⾏情况:
渲染流程图(初次渲染)
图⽚描述
什么时光>鳕鱼简单总结⼀下:
2.调⽤scheduler暴露的⽅法注册需要调度的事件;
3.执⾏DOM插⼊;
4.执⾏uLyaoutEffect或者ClassComponent的⽣命周期函数;
5.浏览器接过控制权,执⾏渲染;
6.scheduler执⾏调度任务,执⾏uEffectDemo;
数字锁定平衡阀
以上就是整体流程,接下来再深⼊⼀点,看看uEffect和uLayoutEffect是怎么解析和执⾏的:
图⽚描述侍讲学士
u(Layout)Effect解析与执⾏
1.解析
图⽚描述
从上图可知,uesEffect和uLayoutEffect最终都会调⽤mountEffectImpl函数,然后初始化/更新Fiber的updateQueue,可以看⼀下mountEffectImpl函数是怎样的:
function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
var hook = mountWorkInProgressHook();
var nextDeps = deps === undefined ? null : deps;
currentlyRenderingFiber$1.effectTag |= fiberEffectTag;
}
都认识,但是不知道是⼲嘛的,好吧,还是⽤⼀张图来说明吧:
图⽚描述
这个函数的功能如下:
1.创建hook对象,放⼊到workInProgressHook链表中;
2.Fiber的updateQueue和上⼀步创建的hook关联,这样每⼀个Fiber对象上就知道要执⾏Effect了;
那么workInProgressHook是⼲嘛的呢,看下源代码的解释吧:
var workInProgressHook = null; // Whether an update was scheduled at any point during the render pha. This
// does not get ret if we do another render pass; only when we’re completely
// finished evaluating this component. This is an optimization so we know
// whether we need to clear render pha updates after a throw.
2.updateQueue数据结构
function commitHookEffectListMount(tag, finishedWork) {
var updateQueue = finishedWork.updateQueue;
var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
if (lastEffect !== null) {
var firstEffect = ;
var effect = firstEffect;
do {
if ((effect.tag & tag) === tag) {
// Mount
var create = ate;
effect.destroy = create();
{
var destroy = effect.destroy;
if (destroy !== undefined && typeof destroy !== 'function') {
var addendum = void 0;学习通登录
if (destroy === null) {
addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).';
} el if (typeof destroy.then === 'function') {
addendum = '\n\nIt looks like you wrote uEffect(async () => ...) or returned a Promi. ' + 'Instead, write the async function inside your effect ' + 'an d call it immediately:\n\n' + 'uEffect(() => {\n' + '  a
sync function fetchData() {\n' + '    // You can await here\n' + '    const respon = Data( someId);\n' + '    // ...\n' + '  }\n' + '  fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Ho oks: fb.me/react-hooks-data-fetching';
} el {
addendum = ' You returned: ' + destroy;
}
error('An effect function must not return anything besides a function, ' + 'which is ud for clean-up.%s%s', addendum, getStackByFiberInDevAndProd (finishedWork));
}
}
}
effect = ;
} while (effect !== firstEffect);
}
}
其实上⾯已经讲了commitHookEffectListMount的执⾏,这⾥再看下具体的执⾏过程:
图⽚描述
执⾏uEffect的⼊⼝:
function commitLifeCycles(finishedRoot, current, finishedWork, committedExpirationTime) {
switch (finishedWork.tag) {
ca FunctionComponent:
ca ForwardRef:
ca SimpleMemoComponent:
ca Block:
{
commitHookEffectListMount(Layout | HasEffect, finishedWork);
return;
}
}
执⾏uLayoutEffect的⼊⼝:
function commitPassiveHookEffects(finishedWork) {
if ((finishedWork.effectTag & Passive) !== NoEffect) {
switch (finishedWork.tag) {
ca FunctionComponent:
ca ForwardRef:
ca SimpleMemoComponent:
ca Block:
{
commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);咸宁旅游景点
break;
}
}
}
}
可以看出两个执⾏⼊⼝传⼊的第⼀个⼊参tag是不⼀样的,最终执⾏的副作⽤函数就区分开来了。
MessageChannel异步调度
现在⼤家应该对uEffect和uLayoutEffect的执⾏有了⼀个⼤致的了解,那么还有⼀个关于scheduler异步调度的⼩问题,本⽂最开始模拟的⼀个例⼦⾥是通过tTimeout来完成的,React中则是通过MessageChannel来实现的,如果不熟悉可以查查使⽤⽅式,这⾥来看下异步执⾏的过程:
图⽚描述
浏览器渲染流程
关于浏览器的渲染这⾥我就以推荐学习资料为主,因为我⾃⼰也没有这些讲解得好,就没必要重复了;
在学习Hooks的时候,难免会和class组件中的⽣命周期做⽐较,这⾥我们只关注uEffect,uEffect在某些程度上相当于componentDidMount 、 componentDidUpdate 、 componentWillUnmount三个钩⼦函数的集合,因为这些函数都会阻塞浏览器的渲染,其中componentDidMount 、 componentDid
Update的执⾏是在哪⾥呢,看⼀下上⾯提到的commitLifeCycles函数就清楚了(componentWillUnmount⼤家有兴趣⾃⼰找找吧);
function commitLifeCycles(finishedRoot, current, finishedWork, committedExpirationTime) {
switch (finishedWork.tag) {
ca FunctionComponent:
ca ForwardRef:
ca SimpleMemoComponent:
ca Block:
{
commitHookEffectListMount(Layout | HasEffect, finishedWork);企业内部管理制度
return;
}
ca ClassComponent:
{
var instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) { // 初次渲染
......
stopPhaTimer();
} el { // 更新渲染
......
stopPhaTimer();
}
}

本文发布于:2023-06-22 21:26:04,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/1016219.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:渲染   函数   浏览器   阶段   学习   节点
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图