拋出一個問題,從輸入
url
地址欄到全部內容顯示到界面上作了哪些事?css
DNS
服務器請求解析該 URL 中的域名所對應的 IP
地址;TCP
鏈接(三次握手);URL
中域名後面部分對應的文件)的HTTP
請求,該請求報文做爲 TCP
三次握手的第三個報文的數據發送給服務器;html
文本並顯示內容;TCP
鏈接(四次揮手);上面這個問題是一個面試官很是喜歡問的問題,咱們下面把這6個步驟分解,逐步細談優化。html
DNS
解析DNS`解析:將域名解析爲ip地址 ,由上往下匹配,只要命中便中止前端
- 走緩存
- 瀏覽器DNS緩存
- 本機DNS緩存
- 路由器DNS緩存
- 網絡運營商服務器DNS緩存 (80%的DNS解析在這完成的)
- 遞歸查詢
複製代碼
優化策略:儘可能容許使用瀏覽器的緩存,能給咱們節省大量時間。java
TCP
的三次握手SYN (同步序列編號)ACK(確認字符)react
第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等 待Server確認。webpack
第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認鏈接請求,Server進入SYN_RCVD狀態。es6
第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,若是正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,若是正確則鏈接創建成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間能夠開始傳輸數據了。web
優化策略:面試
HTTP
協議通訊最耗費時間的是創建TCP
鏈接的過程,那咱們就可使用HTTP Keep-Alive
,在HTTP
早期,每一個HTTP
請求都要求打開一個TCP socket
鏈接,而且使用一次以後就斷開這個TCP
鏈接。 使用keep-alive
能夠改善這種狀態,即在一次TCP鏈接中能夠持續發送多份數據而不會斷開鏈接。經過使用keep-alive
機制,能夠減小TCP
鏈接創建次數,也意味着能夠減小TIME_WAIT
狀態鏈接,以此提升性能和提升http
服務器的吞吐率(更少的tcp
鏈接意味着更少的系統內核調用keep-alive
並非免費的午飯,長時間的TCP
鏈接容易致使系統資源無效佔用。配置不當的keep-alive
,有時比重複利用鏈接帶來的損失還更大。因此,正確地設置keep-alive timeout
時間很是重要。(這個keep-alive_timout
時間值意味着:一個http
產生的tcp
鏈接在傳送完最後一個響應後,還須要hold
住keepalive_timeout
秒後,纔開始關閉這個鏈接),若是想更詳細瞭解能夠看這篇文章keep-alve性能優化的測試結果webScoket
通訊協議,僅一次TCP
握手就一直保持鏈接,並且他對二進制數據的傳輸有更好的支持,能夠應用於即時通訊,海量高併發場景。webSocket的原理以及詳解HTTP
請求次數,每次HTTP
請求都會有請求頭,返回響應都會有響應頭,屢次請求不只浪費時間並且會讓網絡傳輸不少無效的資源,使用前端模塊化技術 AMD CMD commonJS ES6等模塊化方案
將多個文件壓縮打包成一個,固然也不能都放在一個文件中,由於這樣傳輸起來可能會很慢,權衡取一箇中間值cookie
去辨識用戶的多種情況時,使用session
替代,把數據儲存在服務器端或者服務器端的數據庫中,這樣只須要一個cookie
傳輸,節省大量的無效傳輸,並且儲存的數據能夠是永久無線大的。html
文件DOM
樹css
標記,調用css解析器將其解析CSSOM
樹link
阻塞 - 爲了解決閃屏,全部解決閃屏的樣式style
非阻塞,與閃屏的樣式不相關的DOM
樹和CSSOM
樹結合在一塊兒,造成render
樹script
標籤,阻塞,調用js
解析器解析js
代碼,可能會修改DOM
樹,也可能會修改CSSOM
樹DOM
樹和CSSOM
樹結合在一塊兒,造成render
樹layout
佈局 render
渲染(重排重繪)script
標籤的屬性
性能優化策略:算法
link
引入,不須要的使用style
標籤(具體是否須要阻塞看業務場景)webpack4
中也要配置圖片壓縮,能極大壓縮圖片大小,對於新版本瀏覽器可使用webp格式圖片
webP詳解,圖片優化對性能提高最大。webpack4
配置 代碼分割,提取公共代碼成單獨模塊。方便緩存/*
runtimeChunk 設置爲 true, webpack 就會把 chunk 文件名所有存到一個單獨的 chunk 中,
這樣更新一個文件只會影響到它所在的 chunk 和 runtimeChunk,避免了引用這個 chunk 的文件也發生改變。
*/
runtimeChunk: true,
splitChunks: {
chunks: 'all' // 默認 entry 的 chunk 不會被拆分, 配置成 all, 就能夠了
}
}
//由於是單入口文件配置,因此沒有考慮多入口的狀況,多入口是應該分別進行處理。
複製代碼
對於須要事件驅動的webpack4
配置懶加載的,能夠看這篇webpack4優化教程,寫得很是全面
一些原生javaScript
的DOM
操做等優化會在下面總結
TCP
的四次揮手,斷開鏈接RAIL
Responce
響應,研究代表,100ms內對用戶的輸入操做進行響應,一般會被人類認爲是當即響應。時間再長,操做與反應之間的鏈接就會中斷,人們就會以爲它的操做有延遲。例如:當用戶點擊一個按鈕,若是100ms內給出響應,那麼用戶就會以爲響應很及時,不會察覺到絲毫延遲感。Animaton
現現在大多數設備的屏幕刷新頻率是60Hz,也就是每秒鐘屏幕刷新60次;所以網頁動畫的運行速度只要達到60FPS,咱們就會以爲動畫很流暢。Idle
RAIL規定,空閒週期內運行的任務不得超過50ms,固然不止RAIL規定,W3C性能工做組的Longtasks標準也規定了超過50毫秒的任務屬於長任務,那麼50ms這個數字是怎麼得來的呢?瀏覽器是單線程的,這意味着同一時間主線程只能處理一個任務,若是一個任務執行時間過長,瀏覽器則沒法執行其餘任務,用戶會感受到瀏覽器被卡死了,由於他的輸入得不到任何響應。爲了達到100ms內給出響應,將空閒週期執行的任務限制爲50ms意味着,即便用戶的輸入行爲發生在空閒任務剛開始執行,瀏覽器仍有剩餘的50ms時間用來響應用戶輸入,而不會產生用戶可察覺的延遲。Load
若是不能在1秒鐘內加載網頁並讓用戶看到內容,用戶的注意力就會分散。用戶會以爲他要作的事情被打斷,若是10秒鐘還打不開網頁,用戶會感到失望,會放棄他們想作的事,之後他們或許都不會再回來。如何使網頁更絲滑?
使用requestAnimationFrame
避免FSL
先執行JS
,而後在JS
中修改了樣式從而致使樣式計算,而後樣式的改動觸發了佈局、繪製、合成。但JavaScript
能夠強制瀏覽器將佈局提早執行,這就叫 強制同步佈局FSL
。
//讀取offsetWidth的值會致使重繪
const newWidth = container.offsetWidth;
//設置width的值會致使重排,可是for循環內部
代碼執行速度極快,當上面的查詢操做致使的重繪
尚未完成,下面的代碼又會致使重排,並且這個重
排會強制結束上面的重繪,直接重排,這樣對性能影響
很是大。因此咱們通常會在循環外部定義一個變量,這裏
面使用變量代替container.offsetWidth;
boxes[i].style.width = newWidth + 'px';
}
複製代碼
使用transform
屬性去操做動畫,這個屬性是由合成器單獨處理的,因此使用這個屬性能夠避免佈局與繪製。
使用translateZ(0)
開啓圖層,減小重繪重排。特別在移動端,儘可能使用transform
代替absolute
。建立圖層的最佳方式是使用will-change,但某些不支持這個屬性的瀏覽器可使用3D 變形(transform: translateZ(0))來強制建立一個新層。
有興趣的能夠看看這篇文字 前端頁面優化
樣式的切換最好提早定義好class
,經過class
的切換批量修改樣式,避免屢次重繪重排
能夠先切換display:none
再修改樣式
屢次的append
操做能夠先插入到一個新生成的元素中,再一次性插入到頁面中。
代碼複用,函數柯里化,封裝高階函數,將屢次複用代碼封裝成普通函數(俗稱方法),React
中封裝成高階組件,ES6
中可使用繼承,TypeScript
中接口繼承,類繼承,接口合併,類合併。
在把數據儲存在localstorage和sessionstorage
中時,能夠再本身定義一個模塊,把這些數據在內存中存儲一份,這樣只要能夠直接從內存中讀書,速度更快,性能更好。
能不定義全局變量就不定義全局變量,最好使用局部變量代替全局變量,查找的速度要高一倍。
強力推薦閱讀:阮一峯ES6教程
以上都是根據本人的知識點總結得出,後期還會有更多性能優化方案等出來,路過點個贊收藏收藏~,歡迎提出問題補充~
下面加入
React
的性能優化方案:
在生命週期函數shouldComponentUpdate
中對this.state
和prev state
進行淺比較,使用for-in
循環遍歷二者, 只要獲得他們每一項值,只要有一個不同就返回true
,更新組件。
定義組件時不適用React.component , 使用PureComponent代替,這樣React
機制會自動在shouldComponentUpdate
中進行淺比較,決定是否更新。
上面兩條優化方案只進行淺比較,只對比直接屬性的值,固然你還能夠在上面加入this.props
和prevprops
的遍歷比較,由於shouldComponentUpdate
的生命週期函數自帶這兩個參數。若是props 和 state
的值比較複雜,那麼可使用下面這種方式去進行深比較。
解決:
var map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
複製代碼
總結:使用以上方式,能夠減小沒必要要的重複渲染。
React
的JSX
語法要求必須包裹一層根標籤,爲了減小沒必要要的DOM
層級,咱們使用Fragment
標籤代替,這樣渲染時候不會渲染多餘的DOM
節點,讓DIFF
算法更快遍歷。
使用Redux
管理全局多個組件複用的狀態。
React
構建的是SPA
應用,對SEO
不夠友好,能夠選擇部分SSR
技術進行SEO
優化。
對Ant-design
這類的UI
組件庫,進行按需加載配置,從import Button from 'antd' 的引入方式,變成import {Button} from antd
的方式引入。(相似Babel7中的runtime和polifill的區別
).
在React
中一些數據的須要更新,可是卻不急着使用,或者說每次更新的這個數據不須要更新組件從新渲染的,能夠按期成類的實例上的屬性,這樣能減小屢次的重複無心義的DIFF
和渲染。
Redux
的使用要看狀況使用,若是隻是一個局部狀態(僅僅是一個組件或者父子組件使用就不要使用Redux
)。對於一個父子、父子孫多層組件須要用到的state數據
,也可使用context上下文
去傳遞. Context上下文詳解,可是複雜項目的多個不一樣層次組件使用到的state
,必須上Redux
。
全部的原生監聽事件,定時器等,必須在componentWillUnmount
中清除,不然大型項目一定會發生內存泄露,極度影響性能!!!