什么是时间分片(Time Slicing)?

根据W3C性能小组的介绍,超过50ms的任务就是长任务。javascript

图片描述

图片来自使用 RAIL 模型评估性能css

根据上图咱们能够知道,当延迟超过100ms,用户就会察觉到轻微的延迟。html

因此为了不这种状况,咱们可使用两种方案,一种是Web Worker,另外一种是时间切片(Time Slicing)前端

Web Worker

咱们都知道,JS是单线程,因此当咱们在运行长任务时,容易形成页面假死的状态,虽然咱们能够将任务放在任务队列中,经过异步的方式执行,但这并不能改变JS的本质。java

因此为了改变这种现状,whatwg推出了Web Workersgit

具体的语法不会进行说明,有兴趣的童鞋能够查看MDN Web Workergithub

咱们能够看看使用了Web Worker以后的优化效果:web

const testWorker = new Worker('./worker.js')
setTimeout(_ => {
  testWorker.postMessage({})
  testWorker.onmessage = function (ev) {
    console.log(ev.data)
  }
}, 5000)

// worker.js
self.onmessage = function () {
  const start = performance.now()
  while (performance.now() - start < 1000) {}
  postMessage('done!')
}
复制代码

图片描述

代码以及截图来自于让你的网页更丝滑性能优化

时间切片(Time Slicing)

时间切片是一项使用得比较广的技术方案,它的本质就是将长任务分割为一个个执行时间很短的任务,而后再一个个地执行。微信

这个概念在咱们平常的性能优化上是很是有用的。

例如当咱们须要在页面中一次性插入一个长列表时(固然,一般这种状况,咱们会使用分页去作)。

若是利用时间分片的概念来实现这个功能,咱们可使用requestAnimationFrame+DocumentFragment

关于这两个API,我一样不会作详细的介绍,有兴趣的能够查看MDN requestAnimationFrameMDN DocumentFragment

这里有两个DEMO,你们能够对比下流畅程度:

未使用时间分片:

<style> * { margin: 0; padding: 0; } .list { width: 60vw; position: absolute; left: 50%; transform: translateX(-50%); } </style>
<ul class="list"></ul>
<script> 'use strict' let list = document.querySelector('.list') let total = 100000 for (let i = 0; i < total; ++i) { let item = document.createElement('li') item.innerText = `我是${i}` list.appendChild(item) } </script>
复制代码

使用时间分片:

<style> * { margin: 0; padding: 0; } .list { width: 60vw; position: absolute; left: 50%; transform: translateX(-50%); } </style>
<ul class="list"></ul>
<script> 'use strict' let list = document.querySelector('.list') let total = 100000 let size = 20 let index = 0 const render = (total, index) => { if (total <= 0) { return } let curPage = Math.min(total, size) window.requestAnimationFrame(() => { let fragment = document.createDocumentFragment() for (let i = 0; i < curPage; ++i) { let item = document.createElement('li') item.innerText = `我是${index + i}` fragment.appendChild(item) } list.appendChild(fragment) render(total - curPage, index + curPage) }) } render(total, index) </script>
复制代码

没有作太多的测评,可是从用户视觉上的感觉来看就是,第一种方案,我就是想刷新都要打好几个转,往下滑的时候也有白屏的现象。

除了上述的生成DOM的方案,咱们一样能够利用Web Api requestIdleCallback 以及ES6 API Generator]来实现。

一样不会作太多的介绍,详细规则能够看MDN requestIdleCallback以及MDN Generator

具体实现以下:

<style> * { margin: 0; padding: 0; } .list { width: 60vw; position: absolute; left: 50%; transform: translateX(-50%); } </style>
<ul class="list"></ul>
<script> 'use strict' function gen(task) { requestIdleCallback(deadline => { let next = task.next() while (!next.done) { if (deadline.timeRemaining() <= 0) { gen(task) return } next = task.next() } }) } let list = document.querySelector('.list') let total = 100000 function* loop() { for (let i = 0; i < total; ++i) { let item = document.createElement('li') item.innerText = `我是${i}` list.appendChild(item) yield } } gen(loop()) </script>
复制代码

参考资料

  1. web-performance
  2. Measure Performance with the RAIL Model
  3. 让你的网页更丝滑
  4. 「前端进阶」高性能渲染十万条数据(时间分片)

后记

若是你喜欢探讨技术,或者对本文有任何的意见或建议,很是欢迎加鱼头微信好友一块儿探讨,固然,鱼头也很是但愿能跟你一块儿聊生活,聊爱好,谈天说地。 鱼头的微信号是:krisChans95 也能够扫码关注公众号,订阅更多精彩内容。

https://user-gold-cdn.xitu.io/2020/3/4/170a55cc795174aa?w=1000&h=480&f=png&s=311000
相关文章
相关标签/搜索