7分钟教你用时间切片让页面看起来更流畅

一、前置知识

首先,在开始介绍时间切片前,很有必要先把浏览器的渲染流程梳理一下,这里面涉及的知识有event loop渲染帧等知识,下面会简单地介绍。

event loop

事件循环,这个严格来说其实并不是js语言本身的特性,而是在浏览器这个宿主环境下提供的机制,(因为在node环境下又是另一种事件循环机制了),浏览器虽然是多进程,但是本身每个Tag页就是一个子进程,而每个子进程的js都是以单线程执行的,按道理来说就是代码从上往下执行,中间如果有东西卡住了,那么整个js的执行就会阻塞掉。那么诸如setTimeoutsetInterval之类的异步又是怎么回事呢?其实它们并不在js的线程上,而是浏览器交给额外的线程去执行的,执行完触发条件后才会推进任务队列中,供js引擎拿来运行.

event loop

如上所示,当js开始运行时,把任务压入执行栈,然后一个个按顺序执行,当遇到异步任务时,其实会先注册到event table中,(可以理解为一个用来记录异步任务的队列),然后继续执行下一个同步任务。与此同时(因为异步任务是独立于js线程外的线程执行的,不存在阻塞关系),记录在event table里的异步任务会先执行它的前置条件,比如setTimeout设置了多少秒,那么就会开始计时多少秒,达成条件后才会推进去任务队列中排队。当整个js执行栈执行完并清空后,就会去遍历任务队列,拿出里面的宏任务推入到js执行栈中重复上面的步骤。

渲染帧

页面是一帧一帧绘制出来的,当每秒绘制的帧数(FPS)达到 60 时(每秒60帧画面),页面是流畅的,小于这个值时,用户会感觉到卡顿,所以每一帧分到的时间是 1000/60 ≈ 16 ms,那么一帧里浏览器到底干了哪些事呢?请看下图:

frame

这里解释一下每个阶段的含义:

  • events: 点击事件、键盘事件、滚动事件等
  • macro: 宏任务,如 setTimeout
  • micro: 微任务,如 Promise
  • rAF: requestAnimationFrame, 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
  • Layout: CSS 计算,页面布局
  • Paint: 页面绘制
  • rIC: requestIdleCallback,将在浏览器的空闲时段内调用的函数排队调用。

也就是说,event loop也只是一帧里的一小部分,浏览器除了执行js的任务,还会经历rAFlayoutpaint等阶段,当js的执行时间过长,加上绘制等阶段超过了16ms,那么用户就会感觉到明显卡顿。

二、时间切片核心

在理解了event loop 和浏览器每一帧发生的事情之后,我们基本可以知道,页面交互卡顿大多数是因为js线程执行过久,也就是出现了长任务。

而时间切片的核心原理,就是把一段原本要执行100ms的任务,分成10个10ms的任务,分散到每一帧中执行,留足时间给layoutpaint阶段,这样就可以保证肉眼可见的流畅了。需要注意的是,把一个长任务分成多个小任务执行的前提,必须结合event loop的机制来操作,很明显的是,如果多个小的同步任务依然在同一次事件循环中执行,那么依然会阻塞页面的渲染,因此必须合理地把它们一个个安排到下一轮事件循环中。

下面先给出对比效果:

假设不使用时间切片,同步地渲染10万条数据。

不用时间切片

可以看到,直接暴力渲染10万条以上数据时,会卡顿2、3秒,并且直到渲染完成之前,页面上所有元素包括input框都无法交互。

而使用时间切片后,流畅度会有显著提升:

时间切片

加载的速度明显提升,并且交互没有阻塞

需要说明的是,在使用了时间切片的例子中,其实列表并没有加载完的,而是动态地一帧加载一点这样子,留足了时间给浏览器渲染,所以才会看起来前1000条渲染完,但是滚动一下页面会发现,其实后面依然在加载,只是没有阻塞而已。

三、如何使用时间切片

看了以上的对比之后,相信各位都知道时间切片的优势了。但是具体怎么用呢?其实上面已经把思路说得非常直白了,就是把本来需要执行很长时间的js逻辑,分割成一段段小的逻辑,然后一个接一个地推到下一帧里。

手动切片

//假设有个任务要插入10W条数据,大概耗时10s
var listDom = document.getElementById("list");

function bigInsert(){
   
    let i = 0;
    while(i<100000
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值