什麼是時間分片(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
相關文章
相關標籤/搜索