实战篇 - 如何实现和淘宝移动端同样的模块化加载 (task-silce)

事情背景

事情的背景是个人实际项目,在个人实际项目当中,发现首屏渲染的速度比较慢,这样致使白屏的时间会特别长,影响用户体验度,刚好我有一天看了淘宝移动端的加载方式,针对咱们的项目,就作了一次优化调整,并写了一个简单的工具库javascript

这个工具是不限制环境和框架的,我如今的框架是 vue ,在 react 及小程序中也作过测试,是彻底可使用的,使用建议你们仔细阅读这篇文章,但愿能够对你的工做有所帮助前端

性能优化的目的

咱们每一次的界面变化,都要经历如下步骤:vue

人的眼睛大约每秒能够看到 60 帧,那么就表明咱们每 16.7ms 就要看到 1 帧,一帧就要经历上图的 5 步,说明咱们的每个任务(task) 不宜过长,这样就会致使用户对于界面感知的不友好性java

根据谷歌统计的数据,用户在不一样时间段内接收到的反馈,可能直接影响到对于网站的用户留存,以下图:react

在这里咱们不深刻讲对于这方面的一些细节,这篇文章主要是给你们讲一下,若是作任务切片,如何优化界面的渲染速度和响应速度git

分析淘宝

淘宝的渲染方式

咱们先看一下淘宝的渲染方式es6

经过图片和 Performancemain 部分,咱们能够看得出来淘宝移动端的加载方式,是一块一块去加载的,暂时咱们称之为 模块化加载github

performance 的使用和如何查看性能优化的数据,可经过 性能优化篇 - Performance(工具 & api) 来了解 performancenpm

淘宝的任务切片

咱们放大之后能够看的出来,淘宝网在每一次的任务完成后,都会进行上面的 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

相关文章
相关标签/搜索