解析篇 - Task-slice实现淘宝移动端方式加载

6,011 阅读6分钟

前言

继上一篇文章发布之后,由于大家的支持,被顶上了掘金热搜榜第一,这里先感谢大家对我的支持

当然,也有一些大佬,发现了其中的一些问题,在 git 里面也给我提了相应的 issues,感谢你们,大家针对的疑问比较多,而且都希望有一个 demo 可以做演示,看见实际效果,在这里呢,我会拿我实际项目按照大家的疑问去调整,看看是不是真的是有你们考虑的问题

如果有朋友还是不怎么了解 Performance ,那就可以看一下这里,看下面的图就会比较容易了:性能优化篇 - Performance(工具 & api)

工具 git

git地址: task-slice 任务切片

大家如果觉得该文章对大家有一定的帮助的话,还希望大家帮忙多点点 star

问题

疑问一:是否会空转

这个会空转,也确实会空转,但是空转是有作用的,两个 while 配合 generator 才可以达到 任务切片 的效果,这个待会 demo 给大家演示

空转产生的效果就是 空间换时间 的过程

疑问二:没有提速效果,反而代码量增加

在这里呢,我需要强调一下上一章说过的一个特点,就是 响应 ,我们提升的,不只是整体的速度,我们可能整体的渲染速度下来,和之前不会有太大的差别

但是,我们提升了 响应 速度,意味着什么,意味着用户可以在最短时间内对访问我们网站的行为做出最快的反应

举个例子:

之前:我们要加载一个网站,前面的加载资源 2s,渲染需要 500ms(5个组件),那就意味着,我们在 2500ms 以后,才可以进行绘制,也就是说,用户要在 2500ms 以后才可以看到内容

现在:同样的资源加载时间 2s,同样的的渲染时间 500ms,同样的5个组件,但是使用了任务切片后,我们每一个组件都是独立渲染绘制的,也就是说,我们在 2100ms 的时候,就可以让用户看见我们的第一块内容

总体问题

整体来说,大家最主要的问题,都是在为什么要使用 while 去循环那么多遍 yield,而不使用 if,通过 performance 大家其实可以看得出来,只有通过 while 才可以完成切片,这是重点,这也是为什么我要使用 while ,而不使用 if 的原因了

完整 demo

大家等了很久,我已经把 demo 放到了 github 上了,框架使用的是 vue,写了一个很简单的 demo,可以满足大家了解该工具的需求

之前我们处理数据的方式

在之前,我们要实现一个做数据更新,是这样的:

// before
var arr = [0,1,2,3,4,5,6,7,8,9] //模仿接口请求返回的数据
this.arr = arr

我们拿到数据以后呢,直接就把数据更新到对应的响应式数组里面,然后 vue 内部会直接把一整个数组进行遍历和渲染

根据下图,我们就可以看得出来,在之前我们渲染的方式,其实这是一个非常简单的 demo,我们的实际项目,不会只有这么简单的布局,也不会只有这么简单的内容

使用任务切片后我们处理数据的方式

// after
TaskSlice.init({
  sliceList: 10,
  callback: i => {
    this.arr.push(i)
  }
})

我们可以很直观的感受到,对于内容得渲染绘制,有了很大的区别

Main 放大以后,我们就可以看得出来,我们把每一个任务都给切开了,然后计算了样式,直接完成了绘制,把 dom 直接放在了页面上,让用户在第一时间看见了内容

如何做到真正的性能优化

有些性能优化,其实真的和想象当中,不一样

对于性能优化的误解

很多人,对于性能优化来说,都知道减少请求,快速渲染等等,当然,这个没有任何问题

但是,在当下的互联网市场需求来看,我们有很多事情要做,不一定现在这样的方法是最好的,我们要做一定的改变才能做到真正的优化

举个例子

var count = 5
for (var i = 0; i < count; ++i) {
    var div = document.createElement('div')
    document.body.appendChild(div)
}

假设我们要做性能优化的时候,就会发现这个样子写,是不好的,因为这样会不断的重绘和回流,性能肯定不是最好的,所以很多人想到了一个 api:document.createDocumentFragment

使用 document.createDocumentFragment 我们可以创建一个文档碎片,把所有的 div 都放到一个文档碎片内,然后在最后去执行 bodyappendChild

var count = 5
var fragment = document.createDocumentFragment()
for (var i = 0; i < count; ++i) {
    var div = document.createElement('div')
    fragment.appendChild(div)
}
document.body.appendChild(fragment)

很多人一看会觉得这样很好,没问题,这样我们就不需要不断的去重绘和回流了,而且我们使用创建文档碎片的方式,也不会创建多余的 dom

告诉大家,通过 performance 可以知道,这样的整体的 渲染速度 是没有直接去创建快的!!!

其实对于一些简单的需求,这种想法是没有问题的,因为本来内容就少,界面就简单,我们就不需要做任务切片,也不需要做什么太过于复杂的处理,直接的渲染反而会更快

那么问题来了,如果我们要是有 1000 个 dom (包括层级的 dom),然后还有很复杂的样式,还有很多的界面处理,那么这个方法好吗?

直接告诉你,不只是不好,而且还是很不好,这样我们要等 css treedom tree 全部加载完毕,然后合并,计算完样式,最后才能绘制到页面上,很慢很慢,如果要是使用任务切片,假设我们确实有这么多的 dom,我们也会把他们切开,在一个时间段内,去渲染一部分,这样就可以让用户在最短时间内,看到 dom,还是那个重点:我们提升的是针对用户的响应速度,而不是整体的渲染速度

总结

这篇文章主要是给大家解答一下疑惑,让大家去更好的去了解这个工具,并帮助大家运用到实际项目当中

如果大家有疑问,请仔细看一下这篇文章以及 实战篇 - 如何实现和淘宝移动端一样的模块化加载 (task-silce),并结合 demo 和源码去看看是否能解决你的问题,如果解决不了,欢迎及时提问,有时间的话我会回复的

还有大家别只看代码不去测试,有时候你看到想到的,不一定和实际效果一样