事情的背景是个人实际项目,在个人实际项目当中,发现首屏渲染的速度比较慢,这样致使白屏的时间会特别长,影响用户体验度,刚好我有一天看了淘宝移动端的加载方式,针对咱们的项目,就作了一次优化调整,并写了一个简单的工具库javascript
这个工具是不限制环境和框架的,我如今的框架是 vue ,在 react 及小程序中也作过测试,是彻底可使用的,使用建议你们仔细阅读这篇文章,但愿能够对你的工做有所帮助前端
咱们每一次的界面变化,都要经历如下步骤:vue
人的眼睛大约每秒能够看到 60 帧,那么就表明咱们每 16.7ms 就要看到 1 帧,一帧就要经历上图的 5 步,说明咱们的每个任务(task) 不宜过长,这样就会致使用户对于界面感知的不友好性java
根据谷歌统计的数据,用户在不一样时间段内接收到的反馈,可能直接影响到对于网站的用户留存,以下图:react
在这里咱们不深刻讲对于这方面的一些细节,这篇文章主要是给你们讲一下,若是作任务切片,如何优化界面的渲染速度和响应速度git
咱们先看一下淘宝的渲染方式es6
经过图片和 Performance
的 main
部分,咱们能够看得出来淘宝移动端的加载方式,是一块一块去加载的,暂时咱们称之为 模块化加载github
performance
的使用和如何查看性能优化的数据,可经过 性能优化篇 - Performance(工具 & api) 来了解 performance
npm
咱们放大之后能够看的出来,淘宝网在每一次的任务完成后,都会进行上面的 5 步进行界面的渲染,这样可能不如把全部的界面所有渲染完毕后,在进行样式计算、布局、绘制、计算位置等的速度快,可是这样能够保证,让用户在最短的时间内,能够看到咱们的网站内容小程序
简单的介绍一下渲染的步骤和对用户的影响,及淘宝的渲染方式,接下来咱们开始实现一个任务切片的工具
任务切片,顾名思义就是咱们要把每个任务去作切片,缩短任务的执行时长,加快任务的渲染
这里要使用 es6 的 generator
的特性去实现任务切片
function init({ sliceList, callback }) {
if (!isFunction(callback)) {
console.error('callback 为必传参数并为 function');
return;
}
// 添加切片队列
this.generator = this.sliceQueue({
sliceList,
callback
});
// 开始切片
this.next();
}
复制代码
在一开始的时候,咱们须要至少两个参数:
sliceList
或者 sliceCount
: 能够是数组,也能够是数字,数组就是用来切对应的内容去分块,数字就是按次去切片
callback
: 这里须要使用者传一个回调函数,用来通知使用者切片到什么位置
function* sliceQueue({ sliceList, callback }) {
let listOrNum = (isNum(sliceList) && sliceList) || (isArray(sliceList) && sliceList.length);
for (let i = 0; i < listOrNum; ++i) {
const start = performance.now();
callback(i);
while (performance.now() - start < 16.7) {
yield;
}
}
}
复制代码
因为能够接收数组和数字,因此要先作兼容处理
接下来就是核心代码其中之一了:
咱们要记录回调的执行时间,若是执行须要的时间少于 16.7ms,就中止继续执行下去,释放主线程让主线程能够利用这个时间再去作别的事情
若是大于的话,就在下一次绘制的时候去执行
这个时候你们可能会比较好奇,咱们为何要对任务执行时间短的去作切片,时间长的就不切呢?
其实这个要结合下一段代码来看,你们就会了解的比较清楚了
function next() {
const { generator } = this;
const start = performance.now();
let res = null;
do {
res = generator.next();
}
while (!res.done && performance.now() - start < 16.7);
if (res.done) return;
raf(this.next.bind(this));
}
复制代码
有了这段代码,上面最后的长任务的执行没有打断就很好理解了
仍是同样,任务执行的时间少于 16.7ms 就继续执行下一个切片任务
若是要是大于的话,咱们就不须要执行下一个切片了,咱们就要在下一次绘制(requestAnimFrame)的时候,去执行该任务,这样就能够把每个任务给切开了
npm install task-slice
TaskSlice.init(number || array, function(i){
//i 执行到第几回,或者第几个切片任务
})
复制代码
到这里,咱们就能够模仿像淘宝同样的模块化的方式去加载,下图是我本身使用该工具库作的优化先后的数据统计:
很明显,咱们的对于用户的响应速度和界面渲染速度,提高了 50% 左右。
git 地址:github.com/nextdoorUnc…
目前已发布 1.0.0 版本,下一版本可能会支持 promise 或者 控制切片时间,这个看具体的需求,及你们的反馈,我会按期进行对该工具库的更新
该工具已经在 npm 发了包,也在 git 提交了项目,有兴趣的能够去看看,顺便点个 star ,谢谢了。
已经有 n 久没有写过文章了,因为最近工做比较忙,并且项目当中对于前端性能还有架构方面的挑战性仍是比较多的,此次是在作性能优化的时候,作的总结,接下来我会尽可能多分享这种用于实际项目当中的优化方案,感谢你们的支持,谢谢。
还要感谢一下 berwin,是他提出的时间切片给了我灵感,这是他的 git 地址:github.com/berwin