事情的背景是個人實際項目,在個人實際項目當中,發現首屏渲染的速度比較慢,這樣致使白屏的時間會特別長,影響用戶體驗度,剛好我有一天看了淘寶移動端的加載方式,針對咱們的項目,就作了一次優化調整,並寫了一個簡單的工具庫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