延迟渲染解决首页大量Dom渲染导致白屏问题

在我们开发过程中,可能会出现一个页面渲染多个大组件的情况,这里所谓的大组件是指这个组件包含了很多的Dom元素,所以因为一次性渲染过多的dom,导致页面出现白屏的问题。

这里我模拟一个比较大的组件,然后再App组件里面一次性渲染了多个

javascript 复制代码
const HeavyComponent = () => {    return <div>        hello        {            new Array(5000).fill(1).map((item, index) => {                return <div className='item'>                    index;                </div>            })        }    </div>}function App() {    const [count, setCount] = useState(0);    const defer = useDelay();      return <div className='' >    {        new Array(100).fill(0).map((item, index) => {            // return defer(index) && <HeavyComponent/>            return <HeavyComponent/>        })    }      </div>}

这里会明显感觉到首页白屏的情况

1.回顾一下浏览器解析HMTL的过程

当浏览器的网络线程收到 HMLT 文档后,会生成一个渲染任务,并将其传递给渲染主线程的消息队列。

在时间循环机制的作用下,渲染主线程会冲消息队列中取出一个渲染任务,开始渲染流程

渲染流程分为多个阶段:

  1. HMTL解析
  2. 样式计算
  3. 布局
  4. 分层
  5. 绘制
  6. 分块
  7. 光栅化

每个阶段都有明确的输入输入出,上一个阶段的输出变成下一个阶段的输入,形成一个渲染流水线

1.1 解析HTML

从上到下解析html文件,过程中遇到 css 解析 css ,遇到 JS 执行 JS,为了提高解析效率,浏览器在解析之前会预先去交由 与解析线程 下载外部的CSS 和 JS文件

如果主线程在遇到 link 外部CSS时,主线程不会等待,继续执行后面HTML解析,因为下载CSS和解析CSS都不是在主线程中执行的,解析完成生成 CSSOM 后再交给主线程,下载由网络线程完成,解析由负责解析的线程完成,所以这也是CSS不会阻塞HMTL解析的更本原因

如果主线程解析到 Script 标签,会停止HTML的解析,转而等待 JS 文件下载完成,并执行完成后在解析 HTML 。这是因为 JS 代码执行过程中可能会修改当前的DOM 树,所以 DOM 树的生成必须停止,这也是 JS 会阻塞 HTML 解析的根本原因。

第一步完成后,会得到 DOM 树 和 CSSOM 树。

1.2 样式计算

主线程会遍历得到的 DOM ,依照每个节点计算出最终的样式,称之为 样式计算 (Computed Style)

这一过程中会把很多预设值变成绝对值 比如 red 变成 rgb(255,0,0), 相对单位会变成绝对单位(em 、rem、vh、vW)变成 px

这一步完成后一颗带有样式的 树

基于CSSOM 和 DOM树生成 渲染树 ,这里需要主要 CSSOM 和 DOM 是会相互等待的,也就是说必须两个都完成后才开下下一步生成 渲染树,css不会影响 HTML的解析,但是会影响后续的绘制,但是基本不用担心,css 解析成 CSSOM 是非常快的

1.3 布局-- layout

布局阶段会一次遍历每一个DOM 树的节点,计算节点的几何形信息,例如 节点的宽高,在屏幕上的位子

1.4 分层

主线程会使用一套复杂的策略来对整个布局树做分层处理

主要是为了在某些情况下产生了重排,能使得影响到的地方最小化,从而提高效率。

滚动条、堆叠上下文、transform、等样式或多或少会影响分层的结果。

1.5 光栅化

光栅化其实就是对集合图形的 所覆盖的区域,进行像素点着色情况输出

主线程会为了每层单独产生绘制指令集,用于把这一程的内容绘制出来,完成绘制后主线程会讲每一个图层的绘制信息交个合成线程,剩余工作由合成线程完成。

合成线程先对每一个图层进行分块,将其划分为更多的小区域。

分块完成后合成线程就将分块信息交给GPU 进程,完成光栅化,最后绘制到页面上

2.如何使用延迟加载的方式解决

2.1 白屏原因

经过我们了解HTML的解析的过程了解到,JS的解析执行是会影响HTML解析的,加之我们一般的SPA 应用的dom 元素都是靠JS执行生成的,所以在需要一次性 js 生成太多的dom导致白屏。

2.2 解决办法

这是我在网上找的一张关于一帧 生命周期的图片

其中我们主要关注的就是 requestAnimationFiamerequestIdleCallback这两个API调用的时机,一个是在绘制之前调用,一个是在 一帧中有剩余时间是调用,当然具体的一些信息可以去文档看看,这里就不赘述了。

requestIdleCallback 的思想也是React fiber架构中 Schedule 调度模块优先级调度可中断更新的实现思路

本质上来说只要我们不让大量的DOM 在同一时间去渲染就好了,所以我们可以考虑分帧渲染的方法。但是考虑到兼容性相关的问题所以我这里使用了 requestAnimationFiame 来做延迟加载的功能具体完整代码如下

scss 复制代码
const useDelay = (max = 0) => {    const ref = useRef(0);    let rafId: any;    const updateFramecount = () => {        rafId = requestAnimationFrame(()=>{            ref.current ++;            if (ref.current > max){                return;            }            updateFramecount();        })    }    useEffect(() => {        return () => {            if (rafId){                cancelAnimationFrame(rafId);            }        }    }, [])     return function defer(n:any){        // console.log(ref.current, n);        return ref.current  >= n;    }}
const HeavyComponent = () => {    return <div>        hello        {            new Array(5000).fill(1).map((item, index) => {                return <div className='item'>                    index;                </div>            })        }    </div>}function App() {    const [count, setCount] = useState(0);    const defer = useDelay();      return <div className='' >    {        new Array(100).fill(0).map((item, index) => {            return defer(index) && <HeavyComponent/>            // return <HeavyComponent/>        })    }      </div>}

为什么不直接使用setTimeout 或者 setInterval呢

事件循环机制大家应该都有所了解,这里讲讲主要原因

我们时间循环机制中 分为 微任务队列和 宏任务队列,setTimeout 和 setInterval 是进入宏任务列表的,理论上如果我们的微任务列表中一直有任务,或者某个大任务执行时间很长,那么我们的宏任务队列里面就会很久都得不到执行。所以也有可能出现问题。

结尾

最后有兴趣的同学可以试一下,如果有什么问题欢迎大家给我提出宝贵的意见和建议

相关推荐
四喜花露水7 分钟前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy17 分钟前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie1 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust1 小时前
css:基础
前端·css
帅帅哥的兜兜1 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园1 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称1 小时前
购物车-多元素组合动画css
前端·css
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
丶21362 小时前
【鉴权】深入了解 Cookie:Web 开发中的客户端存储小数据
前端·安全·web
Missmiaomiao3 小时前
npm install慢
前端·npm·node.js