你遇到過打開一個網(wǎng)站需要10秒以上的嗎舱禽?這種網(wǎng)頁響應非常緩慢避凝,占用大量的CPU和內(nèi)存舞萄,瀏覽起來常常有卡頓,頁面的動畫效果也不流暢管削。你會有什么反應倒脓?我猜想,大多數(shù)用戶會關(guān)閉這個頁面含思,改為訪問其他網(wǎng)站崎弃。作為一個開發(fā)者甘晤,肯定不愿意看到這種情況,那么怎樣才能提高性能呢饲做?
有興趣的同學可以去我的GitHub线婚,里面有我的分享的學習過程和blog:github.com/193Eric。
使用Web Worker
Web Worker 是HTML5 提供的一個Javascript多線程解決方案艇炎,運行在瀏覽器后臺酌伊。如我們所知js是個單線程的,js引擎在運行js的時候缀踪,不能干其他事。不過在使用中需要注意以下幾點:
它是一種JavaScript 工作線程虹脯,不能直接訪問DOM
能發(fā)送網(wǎng)絡請求驴娃。
它在不用時會被中止,并在下次有需要時重啟
在開發(fā)過程中循集,可以通過 localhost 使用服務工作線程唇敞,但如果要在網(wǎng)站上部署服務工作線程,需要在服務器上設(shè)置 HTTPS咒彤。
Worker 線程無法讀取本地文件疆柔,即不能打開本機的文件系統(tǒng)(file://),它所加載的腳本镶柱,必須來自網(wǎng)絡旷档,這樣做主要是為了安全,因為是可以通訊的歇拆,如果被別人修改了文件鞋屈,引入了本地的js,進行通訊就會出現(xiàn)問題故觅,所以只能訪問同源的網(wǎng)絡文件厂庇。(重點,所以本地調(diào)試的時候需要開啟服務输吏,用node或者后臺語言啟動)
web Worker 與主線程的交互速度
我們用代碼來看下從主線程 --> web worker --> 主線程 最簡單的需要多長時間权旷。
webWorker
var?worker?=?new?Worker("worker.js");
console.log(new?Date().getTime())?//?開始時間
worker.postMessage("123456")
worker.onmessage?=?function?(e)?{
console.log(new?Date().getTime())?//?接收時間
}
</script>
</body>
<html>
//webWorker.js
onmessage=function(evt){
vard=evt.data//通過evt.data獲得發(fā)送來的數(shù)據(jù)
postMessage(d)//將獲取到的數(shù)據(jù)發(fā)送會主線程
}
經(jīng)過幾次測試,大概的事件在30-50ms之間(當然這個值會隨著機器性能問題有大有泄峤Α)拄氯,正常的傳輸速度是很快的。
web Worker 優(yōu)化策略與實踐
案例:大量的js計算(fibonacci數(shù)列計算)
varworker=newWorker("worker.js");
letbeginTime=newDate().getTime()
console.log(beginTime)
letfibonacci=(n)=>{
returnn<2?n:fibonacci(n-1)+fibonacci(n-2);
};
fibonacci(45)
letendTime=newDate().getTime()
console.log(`${endTime}---------歷時${(endTime-beginTime)/1000}s`)
我們在主線程計算fibonacci(45)看看需要多少時間盗迟,在計算的時候頁面是卡主的坤邪,會非常影響用戶體驗。
然后我們看看放在web worker里面是什么情況罚缕。
//index.htmlvarworker=newWorker("worker.js");letbeginTime=newDate().getTime()console.log(beginTime)worker.postMessage("fibonacci");worker.onmessage=function(e){letendTime=newDate().getTime()console.log(e,`${endTime}---------歷時${(endTime-beginTime)/1000}s`)};console.log("working")console.log("working")console.log("working")
//worker.jsonmessage=function(evt){vard=evt.data;//通過evt.data獲得發(fā)送來的數(shù)據(jù)letfibonacci=(n)=>{returnn<2?n:fibonacci(n-1)+fibonacci(n-2);};letdata=fibonacci(45)postMessage(data);//將獲取到的數(shù)據(jù)發(fā)送會主線程}
可以看到艇纺,計算的時間是差不多的,但是并沒有阻礙頁面的運行。所以黔衡,一些高計算耗費時間的功能蚓聘,可以放到web worker中計算完成后再返回回來。
SPA項目首頁白屏問題
現(xiàn)在三大框架(vue,react,angular)的興起盟劫,越來越多的項目采用SPA這種接近原生app體驗的架構(gòu)方式夜牡。采用SPA模式,就意味著侣签,首頁或者main.js過大塘装,首屏加載的速度會受到很大的限制。會存在一定時間的白屏影所。
SPA項目因為html都是js去生成的(虛擬Dom原理)蹦肴,所以在js沒有執(zhí)行加載完之前,dom是不會有的猴娩。一開始客戶端從服務端獲取到html只是一個空殼阴幌,所以會顯示白屏。
下面列出一些解決方案:
1.服務端渲染SSR(加快首屏加載和SEO)
SSR是一個可以有效的提高首屏渲染速度的方法卷中,之后我會開單張去單獨實踐服務端渲染矛双。
2.預渲染
預渲染是一個方案,使用爬蟲技術(shù)蟆豫。由于我們打包過后的都是一些js文件议忽,使用一些技術(shù)(puppeteer)可以爬取到項目在chrome瀏覽器展示的頁面,然后把它寫入js,和打包文件一起无埃。類似prerender-spa-plugin
不過這種技術(shù)的缺點也很明顯徙瓶,我們在打包過程中,所展示的頁面是當時環(huán)境下的數(shù)據(jù)嫉称,也就是說如果首頁有很多的動態(tài)獲取的數(shù)據(jù)(比如說實時的股票價格)侦镇,那如果采用這種方案,用戶第一眼看到并不是當時數(shù)據(jù)织阅,會認為是個錯誤信息壳繁。
3.骨架屏(占位塊和圖片)
骨架屏可以說現(xiàn)在比較實用的首屏優(yōu)化技術(shù)了,方便也不是很復雜荔棉。骨架屏類似于app的啟動頁闹炉,一般我們進入app都會有宣傳的啟動頁,然后才看到首頁润樱。骨架屏就是這樣渣触,用戶進來首先看到的是骨架屏,然后首頁渲染完之后壹若,再把骨架屏干掉嗅钻。
骨架屏主要有兩種方式可以完成:
通過編譯的時候自動生成還是puppeteer(上面說了預渲染也用皂冰,它主要是能在代碼里面喚起chrome瀏覽器,或者后臺喚起养篓,也就是沒有彈出瀏覽器秃流,但是可以獲取到真實瀏覽器中渲染的內(nèi)容),有了它我們就能獲取到瀏覽器里面的dom元素柳弄,然后通過對頁面中元素進行刪減或增添舶胀,對已有元素進行修改生成我們想要的骨架,然后將修改后的 HTML 和 CSS 樣式提取出來碧注,這樣就是骨架屏了嚣伐。當然遇到了圖片的話,還是需要轉(zhuǎn)成svg的形式萍丐,或者base64這樣就不需要網(wǎng)絡請求了纤控。
通過設(shè)計圖+布局這是一種直接重新寫一個頁面的方式,圖片的話采用svg和base64的形式碉纺,生成一個dom,解析注入到服務器返回的index.html里面(ssr render注入)。
現(xiàn)在網(wǎng)絡上的項目刻撒,最能提現(xiàn)的就是餓了么的h5了骨田,我們可以通過手機瀏覽器打開餓了么。
我們首先看到是骨架屏声怔,然后過一會才會看到首屏态贤,這就是餓了么用了這種技術(shù),很大的提高了用戶的體驗醋火。page-skeleton-webpack-plugin項目地址:https://github.com/ElemeFE/page-skeleton-webpack-plugin
SSL延遲有多大悠汽?
我們知道http是經(jīng)過TCP三次握手就可以成功通訊 。再來看Https鏈接芥驳,它也采用TCP協(xié)議發(fā)送數(shù)據(jù)柿冲,所以它也需要上面的這三步握手過程。而且兆旬,在這三步結(jié)束以后假抄,它還有一個SSL握手。
HTTP耗時 = TCP握手 ; HTTPS耗時 = TCP握手 + SSL握手
在研究ssl延遲之前丽猬,我們先來學一個命令行工具curl宿饱,-w,–write-out參數(shù)。顧名思義脚祟,write-out的作用就是輸出點什么谬以。curl的-w參數(shù)用于在一次完整且成功的操作后輸出指定格式的內(nèi)容到標準輸出。
主要了解下幾個參數(shù):
http_code http狀態(tài)碼
time_total 總時間由桌,按秒計为黎。
time_namelookup DNS解析時間,從請求開始到DNS解析完畢所用時間
time_connect 連接時間,從開始到建立TCP連接完成所用時間,
time_appconnect 連接建立完成時間邮丰,如SSL/SSH等建立連接或者完成三次握手時間。
我們主要用time_connect(TCP握手的耗時)碍舍,time_appconnect(SSL握手的耗時)
終端輸入curl -w "TCP握手的耗時: %{time_connect}, SSL握手的耗時: %{time_appconnect}\n" -so /dev/null https://www.taobao.com
我們可以看到ssl耗時是tcp的三倍左右柠座,具體數(shù)字取決于CPU的快慢和網(wǎng)絡狀況。
所以片橡,如果是對安全性要求不高的場合妈经,為了提高網(wǎng)頁性能,建議不要采用保密強度很高的數(shù)字證書捧书。
防抖節(jié)流那些事
在開發(fā)過程中我們很多時候會碰到需要防抖和節(jié)流去解決問題吹泡。不要小看它們,沒有它們的話经瓷,特定情況下的會造成很大的性能阻塞爆哑。
防抖(當持續(xù)觸發(fā)事件時,一定時間段內(nèi)沒有再觸發(fā)事件舆吮,事件處理函數(shù)才會執(zhí)行一次)防抖我們可以理解為揭朝,存在一個scroll事件,只要滾動就會觸發(fā)handler回調(diào)函數(shù)色冀,防抖的處理是潭袱,等scroll事件完全停下來1000ms(這個值自行設(shè)定)之后,再出觸發(fā)handler函數(shù)锋恬。
節(jié)流(當持續(xù)觸發(fā)事件時屯换,保證一定時間段內(nèi)只調(diào)用一次事件處理函數(shù))節(jié)流顧名思義,觸發(fā)scroll事件的時候与学,并不立即執(zhí)行handle函數(shù)彤悔,每隔1000ms(這個值自行設(shè)定)才會執(zhí)行一次handle函數(shù)
區(qū)別:節(jié)流是每一個時間段之后就會觸發(fā)一次,存在間隙的連續(xù)觸發(fā)索守,而防抖不一樣晕窑,只會在最后才觸發(fā)一次。
重排與重繪的相愛相殺
從前面的文章(瀏覽器渲染過程剖析)已經(jīng)學到了網(wǎng)頁的生成過程蕾盯。
HTML代碼轉(zhuǎn)化成DOM
CSS代碼轉(zhuǎn)化成CSSOM(CSS Object Model)
結(jié)合DOM和CSSOM幕屹,生成一棵渲染樹(包含每個節(jié)點的視覺信息)
生成布局(layout),即將所有渲染樹的所有節(jié)點進行平面合成
將布局繪制(paint)在屏幕上
1-3步都很快级遭,耗時的是第四步和第五步望拖。這兩步合稱”渲染“,在網(wǎng)上生成的過程中,網(wǎng)頁至少會渲染一次挫鸽。
有三種情況會導致瀏覽器重新渲染:
1.修改DOM
2.修改樣式表
3.用戶事件(比如鼠標懸停说敏、頁面滾動、輸入框鍵入文字丢郊、改變窗口大小等等)
重新渲染盔沫,就需要重新生成布局和重新繪制医咨。前者叫做"重排"(reflow),后者叫做"重繪"(repaint)架诞。
重排和重繪不斷的觸發(fā)拟淮,是影響網(wǎng)頁性能低下的根本原因。提高網(wǎng)頁性能谴忧,就是要降低"重排"和"重繪"的頻率和成本很泊,盡量少觸發(fā)重新渲染。重排必定會重繪沾谓,而重繪不一定重排委造。
最后
喜歡小編的可以點個贊關(guān)注小編哦,小編每天都會給大家分享文章均驶。
我自己是一名從事了多年的前端老程序員昏兆,小編為大家準備了新出的前端編程學習資料,免費分享給大家妇穴!
如果你也想學習前端爬虱,加入此Q群:950919261