你們知道。咱們天天都在談前端性能優化,天天都在背前端性能優化方案,然而,咱們殊不知道他背後的原理以及涉及那些知識儲備。因此,我問了本身一些問題,css
當我問了本身一些問題之後,我從新學習性能優化的脈絡就很清晰了,接下來咱們來咱們逐一解決。html
在前端的職業生涯中,咱們老是能一次次聽到「性能」和「體驗」這兩個詞。而在慢慢從菜鳥一點點打怪升級的過程當中,這兩個詞聽到的頻率在逐步上升。前端
而不少人只知道,這個東西,面試要考,因而開始背!背!背! 卻從未想過,咱們作性能優化的初衷是什麼。項目在什麼階段應該怎去作什麼樣的優化。怎樣去權衡可維護性和優化的平衡點。這其實都是咱們去作性能優化須要思考的問題,那麼有了大量的思考,其實咱們心中的答案也就能呼之欲出。這也是我要叮囑本身的:作一些事情以前,要去思考他背後本質是什麼,而不是流於表面,人云亦云。vue
那麼咱們爲何要去作性能優化呢?什麼樣的項目必需要去作性能優化?什麼樣的項目能夠犧牲一些優化的點,而換取項目穩定性,和可維護性!node
咱們知道,一個網站最重要的的就是用戶,有了用戶你纔能有業務,打個比方,你是一個電商網站,那麼你必定但願你的用戶很是多,只有這樣你纔能有更多的人去瀏覽你的商品,從而在你的網站上花錢,買東西,這樣你才能產生收益,在好比,爲了獲取更多的用戶,你必需要在藉助第三方的工具去推廣你的網站,好比搜索引擎。而如今的搜索引擎,他都會去對你的網站作性能評估,從而可能影響你的排名!react
如此一來,你就會知道,咱們所謂的性能優化其實就是留住用戶,以及獲取用戶,那麼,基於以上思路,你就能根據當前項目,判斷出我當前網站應該給予什麼樣的特殊優化方式,以及那些通用優化方式,而不是在網上找一通,而後對着優化!webpack
知道了上述問題以後,咱們則須要找尋一些標準,以及性能瓶頸。來達到優化的目的css3
記得亞馬遜作個一個調查,他發現一個網站每100ms的延遲則致使1%的銷量損失,那麼,咱們的網站延遲要到什麼地步纔算性能好呢? 因此咱們要有一個標準!nginx
上述說過,每一個項目的狀況不同,咱們不能跟亞馬遜同樣,作到極致,因此,大多數網站這須要遵循一個標準,咱們認爲達到這個性能指標,就算能夠了,在某個高頻操做的點,在作針對的優化!web
測量頁面的加載性能是一項艱難的任務。所以 Google Developers 正和社區一塊兒致力於創建漸進式網頁指標(Progressive Web Metrics,簡稱 PWM’s)。
PWM’s 都是些什麼,咱們爲何須要它們?
這就牽扯到瀏覽器的歷史了。爲了能徹底的講明白這一塊,咱們從頭講起,在好久好久之前,咱們有兩個主要的點(事件)來測量性能:
DOMContentLoaded — 頁面加載完成但腳本文件剛剛開始執行時觸發這裏指初始的 HTML 文檔加載並解析完成,但不包括樣式表、圖像和子框架的加載完成,參考 MDN DOMContentLoaded 事件
load 事件在頁面徹底加載後觸發,此時用戶已經可使用頁面或應用。
拿掘金舉例,最下部的地方就能夠看見DOMContentLoaded、load
而時至今日在交互複雜,頁面內容複雜的今天,你會發現DOMContentLoaded、load並不像之前那樣能真實反映出用戶的體驗了。他跟你頁面的複雜度、交互難度、動畫多少、等都有不少聯繫,拿bilibili和掘金舉例
你會發現,bilibili遠比掘金的加載時間長的多。可是嗶哩嗶哩的性能卻不比掘金差
在如今的web頁面中,DOMContentLoaded 的問題在於不包含解析和執行 JavaScript 的時間,若是腳本文件太大,那麼這個時間就會很是長。好比移動設備,在 3G 網絡的限制下測量跟蹤時間軸,就會發現要花費差很少十秒才能到達 load 點。 另外一方面,load 事件太晚觸發,就沒法分析出頁面的性能瓶頸。 因此咱們可否依賴這些指標?它們到底給咱們提供了什麼信息? 並且最主要的問題是,從頁面開始加載直至加載完成,用戶對這個過程的感知如何?
你刷新嗶哩嗶哩頁面後,你會發現嗶哩嗶哩的體驗很是好,除了有着好的設計以外,他還有着不少針對的性能優化。好比,快速加載首屏,其餘屏幕懶加載,善於利用緩存等。
說了這麼多什麼叫作 PWM’s呢?
PWM’s 是一組用來幫助檢測性能瓶頸的指標。除開 load 和 DOMContentLoaded,PWM's 給開發者提供了頁面加載過程當中更多更詳細的信息
其實在谷歌瀏覽器中,咱們就能使用devtools 來查看各類指標的加載時間!咱們仍是拿嗶哩嗶哩舉例
首先打開performance 點擊刷新按鈕,
如此一來咱們就能看到一些關鍵的渲染節點
這是谷歌的開發者工具還給咱們提供了一個指標 - FP。這個指標表示頁面繪製的時間點,換句話說它表示當用戶第一次看到白屏的時間點,(fp還有另一個意思就是函數式),FP 事件在 Graphic Layer 進行繪製的時候觸發,而不是文本、圖片或 Canvas 繪製的時候,因此,這個時間點用來測量性能,着實有點棘手,因而,谷歌還給咱們提供了另外一個。
這是當用戶看見一些「內容」元素被繪製在頁面上的時間點。和白屏是不同的,它能夠是文本的首次繪製,或者 SVG 的首次出現,或者 Canvas 的首次繪製等等。
FCP 事件在文本(正在等待字體文件加載的文本不計算在內)、圖片、Canvas 等元素繪製時被觸發。結果代表,FP 和 FCP 的時間差別可能從幾毫秒到幾秒不等。這個差異甚至能夠從上面的圖片中看出來。
因此你的內容繪製時間過長,這說明你的資源文件可能過大,或者網絡拉胯。他能真是的反應網頁性能方面的一些問題
LCP一種新的性能度量標準,LCP 是一種側重於用戶體驗的性能度量標準,與現有度量標準相比,更容易理解與推理。 他與被捨棄的FMP不一樣的是,FMP是有意義的內容繪製時間點,那麼這個有意義的斷定就放在了谷歌開發者工具這塊,他是有爭議的,並且並不能說明性能問題。
W3C Web性能工做組的討論和Google的研究,發現度量頁面主要內容的可見時間有一種更精準且簡單的方法是查看 「繪製面積」 最大的元素什麼時候開始渲染。這個最大的篇幅的內容渲染他極有多是主要內容。因此,瓜熟蒂落的代替了fmp
接下來咱們再來看這一張圖
上面的一張張圖片表示在各個時間點,當前網頁渲染的快照,而藍色的折線,則是當前網頁的內存佔用,從當前折線你能夠清晰的看出垃圾回收(gc)在何時開始的,若是你看當前的藍色折線是一直無限上升的,那麼極有多是發生了內存泄露了,他能很好的幫你定位問題。
咱們點開這裏面的main 選項,就能清晰的知道,當前頁面中全部長任務,以及渲染任務的耗時,和執行順序(所謂長任務:長任務就是指解析、編譯或執行 JavaScript 代碼塊)咱們知道js和渲染是互斥的,因此在圖中也能夠清晰的體現到,他們互斥的關係以及誰妥協誰。這個圖他還有一個洋氣的名字叫--火焰圖
那麼經過他,仔細看
就能看到有個紅色的三角,這就是谷歌工具給咱們的提示,表示性能不達標了!經過些就能分析出一些關鍵的性能瓶頸和可優化的點。
有不少小夥伴會就會說了,這又不是個人網站,我看他幹啥,我只想看看這個網站到底性能如何!谷歌也給咱們提供了一個工具--lighthouse 以前須要下載,如今被開發者工具集成了
一樣拿嗶哩嗶哩來舉例,在開發者工具中測量一下,以後就會返回一堆數據,咱們只關注performance部分,首先他會有一個整體得分,而後就是各個項的時間,接下來逐個分析一下
在這裏咱們只關注這四點 尤爲須要關注第一點和第三點。 嗶哩嗶哩中咱們看他的first contentful paint性能還能夠,若是是紅色的那麼就是超標了。 Speed Index 其實就是速度指數,在谷歌給的標準中,速度指數的標準是4秒 而嗶哩嗶哩的4.3秒也還湊活。
再往下看就是一些須要優化的項,包括http、js、css層面的優化。
在以前的first contentful paint 中,咱們發現他的時間消耗主要有兩部分組成,第一個是渲染時間,另外一個就是網絡加載時間。
咱們打開network 查看一下網頁資源的加載圖,一樣的這個圖也有一個很專業的名字,叫作瀑布圖
他很是直觀的描述了網站資源的加載時間和順序,這個圖呢有兩個解讀方法,一個是橫向看,另外一個是縱向看。 橫向看咱們能看到具體加載的資源
咱們看其實下載資源是最後一個步驟,他還包括等待時間,他須要排隊,耽誤了5毫秒,可能因爲達到了瀏覽器最大請求數量阻塞了200毫秒。接下就是發送用了0.18毫秒。TTFB時間--是後臺的處理時間以及網絡傳輸時間,咱們才能下載
縱向看就能看到資源的加載順序,那麼我就有可能讓某些時間長的請求,提早加載,並行請求,來達到優化的目標。
在理解了上面這些概念以後,咱們再來了解一下Chrome 團隊提出了 RAIL 模型。
RAIL 是 response (響應)、 animation(動畫)、idle(瀏覽器空置狀態)和 load(加載)。
複製代碼
固然這只是一個參考,在複雜的項目中,咱們只能儘可能的追趕,卻很難達到,由於除了項目比較複雜以外,還有不少事情不是咱們能作主的。好比,網站支不支持http2,你也說的不算。若是idle 咱們須要達標,理論上說,洗數據的js計算,後端能作的毫不讓前端去作,然而現實是,在個人職業生涯中,基本天天都在洗數據。
性能優化咱們天天都在談,忽然有一天我我回過頭來思考,咱們天天談的性能優化,真的是在談這些優化的點嗎?不是否是。咱們說之因此能作性能優化,實際上是在深刻了解各個方向,好比 http工做機制、緩存機制、瀏覽器工做原理、工具鏈的優化策略、前端框架的原理以後總結出來的一個方案。因此咱們天天都在談的的性能優化實際上是一個深不見底的池子,他須要你有完善的知識體系以及豐富的經驗,毫不是背背優化點就能搞明白的。
如此一來,咱們之後在談起性能優化,就不要在說這些優化點這些庸俗的東西。而是要深究他背後的原理、和總結出來這個方案的思考。 接下來,咱們一個個來攻克。
關於瀏覽器的工做原理這塊以前寫過一篇文章,基本涵蓋到了一些細節知識點 重學前端(三)-聊聊咱們的瀏覽器的那些事
如上圖所示,他其實就分爲這麼幾步
整個的過程就是從 URL 轉換爲 Bitmap 的過程,先發送請求到服務器,而後服務器返回 HTML,瀏覽器解析 HTML,而後構建 DOM 樹,計算 CSS 屬性,而後進行排版,最後渲染成位圖,而後通過操做系統或硬件的 API 完成視圖的顯示。
那麼在這幾個步驟中,有一個layout 和render 兩個步驟,其實也就是佈局(迴流)和繪製,這也是瀏覽器關鍵渲染路徑中兩個很是重要的步驟,並且很是消耗瀏覽器資源。而咱們的性能優化其實就能夠在這兩個步驟中作文章。
對於佈局而言,咱們須要改變的實際上是元素的幾何信息,好比寬高,和位置,接下來我看看,有哪些操做能夠出發佈局,這樣一來是,就有可能在我麼的代碼中去避免這個操做。從而達到性能優化的目的
添加刪除元素
操做styles
display:none
offsetLeft、scrollTop、clientWidthd等
移動元素位置
修改瀏覽器大小、字體大小等
咱們仍是來看嗶哩嗶哩的火焰圖。紫色的部分就是layout。而在佈局的過程當中有一個很是經典的問題,叫作佈局抖動,從而致使頁面顯得很是卡頓,其實所謂佈局抖動就是連續發生layout 過程致使的
而咱們怎樣能在保證效果的同時在這一步去作性能優化呢?
一、避免迴流
好比修改元素位置,那麼咱們可使用css動畫去解決,利用複合步驟去解決問題,在好比利用vdom 最小限度的去改變元素的佈局,
二、讀寫分離
其實就是利用瀏覽器的api--requestAnimationFrame去在當前針讀數據。下一幀寫數據,這樣就能達到讀寫分離的效果了,在社區上有一個fastdom的庫就能給咱們解決這個問題。
二、避免重繪 對於繪製而言,它只是影響元素的外觀,風格,而不會影響佈局的,好比background-color。
上圖所示,綠色部分就是繪製步驟,而瀏覽器爲了提升新能,在繪製的步驟上開發了複合線程,他就相似於ps 的圖層,瀏覽器也將一些盒模型分爲一個個圖層,這樣一來,修改一些圖層,並不會影響其餘頁面的繪製個佈局。
如上圖所示,就是複合過程,他引發了樣式計算,可是去沒有走重繪,而是一個composite layers 過程,
那麼咱們怎樣儘可能的使用複合,而避免重繪呢?
三、減緩高頻事件的觸發 在複雜的網頁交互中,好比拖動,滾動,高頻點擊,他的觸發頻率很是高,遠遠的高於60hz 因此,咱們就在想真的有必要嗎?因此,咱們就須要有防抖、節流函數等來幫助咱們減緩高頻事件!他的原理也很簡單,其實就是利用定時器,來延時或者間斷的處理事件回調
四、利用瀏覽器api減小頁面抖動
咱們知道react16有個fiber的架構,他就是在底層運用瀏覽器的api requestIdCallback 來實現任務調度,從而最大限度的解決了頁面卡頓問題。那麼咱們在解決卡頓問題的時候,是否是也能夠考慮使用這些requestIdCallback、requestAnimationFrame等這些api呢!
在框架橫行的今天,react、vue、angular 三分天下,可是在框架的編程範式下,咱們每每忽略了本身代碼層面的性能優化,總認爲框架的做者會考慮這些問題,好比react fiber。然而,咱們不知道的是做爲一個框架,讓你的代碼具備可維護性,和這個框架具備普適性,從而推廣開來是很是重要的,因此,框架給咱們的保證是,在不須要手動優化的狀況下,依然能夠給提供過得去的性能。而不是很是好的性能,他其實本質上也是操做dom,只是這個事情框架給你作了,你只須要描述你的目的便可
舉個例子:
如上圖,我最近的vue項目中,就有着嚴重的性能瓶頸,咱們能夠看到,在首次渲染以後,有兩個很是長的長任務在阻塞頁面渲染,顯得很是卡頓,他其實本質緣由就是,table表格渲染,這種大數據量的渲染是很是容易出現性能瓶頸的。雖然vue 有虛擬dom 和diff 算法兜底,可是,他們也不是免費的,也有着不小的開銷。因此,這時候就須要咱們去手動優化,好比加入虛擬滾動。
接下來仍是從底層去理解,js 執行的開銷到底在哪?
如上圖所示,js APP.js這個文件的的編譯、解析時間有700多毫秒。
而再日後看的咱們發現除了有編譯、解析腳本還有gc(垃圾回收)都有很多耗時。如此,咱們就有辦法在這個步驟中去作文章,
接下來咱們逐一解析
前三個,都是一些比較明顯的問題,就再也不贅述,咱們重點解釋應該怎樣寫出迎合瀏覽器的代碼呢?
咱們知道,在瀏覽器中,js的解析引擎叫作v8,其實呢,v8在底層解析的時候,是作了不少事情的,好比,咱們知道tcp 是流的傳輸,因而v8就有了腳本流的優化,解釋起來大體意思就是,v8會給咱們作預解析,在腳本還沒下載完成以後就開始解析代碼,在好比,字節碼緩存,以及懶解析,而咱們要作的就是,就是去迎合瀏覽器,好比:
以前在代碼層面上,咱們列出了能夠作的優化,然而它帶來的收益甚微,可謂微不足道,其實,你仔細看一些vue、element 等知名的開源庫,他們沒有一個執行上述的優化手段的,由於,他們須要爲了可維護性,來犧牲少量的性能是很是划算的。那麼咱們在寫代碼時候,其實也須要作一些平衡以及取捨,你能夠爲了可維護性等理由,而放棄這些優化手段,可是,你卻要必須知道,爲啥要使用這些優化手段,這樣才能在面試,以及未來某個項目中能合理的用上。
而咱們在資源方面優化,他是結結實實能看的見的都東西,而且能看到確切的效果。好比文件資源的壓縮與合併,那圖片格式瀏覽器解析最快,不須要當即加載的圖片能不能懶加載,字體會不會影響性能?
咱們知道在網絡協議層面,資源越小,那麼就表明他的傳輸時間越少,因而,咱們就必需要在文件資源的層面去作一些優化,其實無論怎麼優化,他們總遵循這一個原則:
那麼圍繞這兩點,其實已經有了不少經優化手段了,我說幾種,不少老前端,指定是難以忘懷,只不過在因爲網速和技術的進步,他們被淹沒在歷史的長河中,好比雪碧圖,使用gulp 壓縮合並html、css 、js 資源,使用imagemin 優化圖片大小。,固然這些問題其實,在工程化的今天,他實際上是掩蓋了這些優化,咱們只須要專一開發,描述目的便可,可是有一些,問題咱們仍是要注意:
在上文中,咱們講了一些優化方案,可是無論什麼方案,他都繞不開工具鏈的合理使用,這也是咱們的性能優化中不可繞開的一個環節,由於工具的使用得當,能兼顧可維護性,和比較好的性能,那麼提起工具鏈,就繞不開新一代的構建工具,webpack、rollup等。今天咱們就來探討一下webpack這個老牌構建工具。
其實要提及的初衷實際上是爲了開發者能使用上一些語言的新性能又讓瀏覽器能運行開發者編寫的代碼而起到的中間人的一個身份。只不過因爲自己的強大的插件和loder能力,順帶的給咱們的性能優化作了。具體的優化方式請移步我以前的文章webpack優化解決項目體積大、打包時間長、刷新時間長問題!
這個手段是收益最大的一個手段,上述的手段其實能力有限,而在傳輸層面作文章,其實才是事半功倍的的手段
咱們知道,雖然咱們的代碼壓縮了,可是他不是個壓縮包啊,因而gzip橫空出世,那麼咱們就來看看gzip是個啥?
Gzip是若干種文件壓縮程序的簡稱,HTTP協議上的GZIP編碼是一種用來改進WEB應用程序性能的技術。大流量的WEB站點經常使用GZIP壓縮技術來讓用戶感覺更快的速度。這通常是指WWW服務器中安裝的一個功能,當有人來訪問這個服務器中的網站時,服務器中的這個功能就將網頁內容壓縮後傳輸到來訪的電腦瀏覽器中顯示出來.通常對純文本內容可壓縮到原大小的40%.這樣傳輸就快了,效果就是你點擊網址後會很快的顯示出來.固然這也會增長服務器的負載. 通常服務器中都安裝有這個功能模塊的。
複製代碼
可是不是每一個瀏覽器都支持gzip的,若是知道客戶端是否支持gzip呢,請求頭中有個Accept-Encoding來標識對壓縮的支持。客戶端http請求頭聲明瀏覽器支持的壓縮方式,服務端配置啓用壓縮,壓縮的文件類型,壓縮方式。當客戶端請求到服務端的時候,服務器解析請求頭,若是客戶端支持gzip壓縮,響應時對請求的資源進行壓縮並返回給客戶端,瀏覽器按照本身的方式解析,在http響應頭,咱們能夠看到content-encoding:gzip,這是指服務端使用了gzip的壓縮方式。
使用方式也很是簡單,在一些nginx 、node、等web服務器上啓用便可。
HTTP協議的Keep-Alive意圖在於短期內鏈接複用,但願能夠短期內在同一個鏈接上進行屢次請求/響應。
複製代碼
一般一個網頁可能會有不少組成部分,除了文本內容,還會有諸如:js、css、圖片等靜態資源,有時還會異步發起AJAX請求。只有全部的資源都加載完畢後,咱們看到網頁完整的內容。然而,一個網頁中,可能引入了幾十個js、css文件,上百張圖片,若是每請求一個資源,就建立一個鏈接,而後關閉,代價實在太大了。
基於此背景,咱們但願鏈接可以在短期內獲得複用,在加載同一個網頁中的內容時,儘可能的複用鏈接,這就是HTTP協議中keep-alive屬性的做用。
而在http1.1以後,keep-alive默認開啓,也就是咱們不用特殊關注他了。
如上圖所示,就是一個http 緩存的流程圖,在我看來,全部的優化手段都比不上緩存所帶來的體驗,他直接省去了一些靜態文件的請求資源的開銷,從而在第二次請求是帶來質的提高。而他遵循的規律只有兩點:
而咱們還能夠將緩存和工具鏈鏈接起來,從而給用戶一個更好的體驗,舉個例子:webpack在打包時候,能夠監聽文件是否變化,從而若是文件變化,將改變當前文件名的hash值,其他不變,如此一來,在部署升級以後,用戶也只請求到變更文件,從而減小資源的下載。從而達到性能的最優。
一個服務器與瀏覽器之間的中間人角色,若是網站中註冊了service worker那麼它能夠攔截當前網站全部的請求,進行判斷(須要編寫相應的判斷程序),若是須要向服務器發起請求的就轉給服務器,若是能夠直接使用緩存的就直接返回緩存再也不轉給服務器。從而大大提升瀏覽體驗。
複製代碼
它有着兩個特色
然而不幸的時,在2021年的今天,他的兼容性還堪憂。因此沒有普及。
srr技術實際上是一個很是老牌的技術,他其實很早都存在,只不過因爲vue 、和react 的大火、將傳統的ssr革新掉,讓前端也能參與到srr的這個浪潮中來。他的原理其實很是簡單,就是將首屏內容在服務端拼接爲字符串,放在客戶端解析。從而減小了客戶端的js執行時間。快速渲染頁面。達到性能優化的目的
在上述全部的問題搞定以後,咱們在文章的開頭,提出的最後兩個問題,也就清晰了,原理方面其實也是闡述了個大概。
其實在每一個方向上都有值得深挖的知識,這些須要被深挖的知識在時時刻刻提醒咱們:咱們真的很菜,然而,我常常發現,不少人真的只是流於表面,他享受這互聯網帶來的紅利和錯覺,誤認爲本身很強,而且總愛指點江山。在經歷了很長時間的思考之後,我問本身,若是被過渡擡高的互聯網行業熱度一過。我還剩下什麼,其實也就會用vue、react的api而已。
故記錄此文,並朝着各個方向慢慢攻克,但願給你們在知識體系上添磚加瓦是不對之處。請批評指正!