浏览器原理及性能优化
1. 影響頁面性能的因素
- HTTP 請求
- 復雜的頁面邏輯
- 重度的DOM操作
- 服務端響應
- 大量的數據
- …
2. 優化網頁性能
- 資源壓縮與合并(代碼打包)
- 異步加載
- CDN
- DNS 預解析
- 緩存
- …
一、瀏覽器
1.1 主要作用
瀏覽器的主要功能就是向服務器發出請求,在瀏覽器窗口中展示選擇的網絡資源。這些網絡資源包括以下內容:
- HTML
- CSS
- JavaScript的
- 媒體(圖片,視頻等)
也可以分為HTML文檔(HTML/CSS/JS)、PDF、圖片、視頻和其他類型
1.2 組成結構
用戶界面(User Interface) - 包括地址欄、前進/后退按鈕、書簽菜單等。除了瀏覽器主窗口顯示的您請求的頁面外,其他顯示的各個部分都屬于用戶界面。
瀏覽器引擎(Browser engine) - 在用戶界面和渲染引擎之間傳送指令。
渲染引擎(Rendering engine) - 負責顯示請求的內容。如果請求的內容是 HTML,它就負責解析 HTML 和 CSS 內容,并將解析后的內容顯示在屏幕上。
也可以叫呈現引擎(Rendering Engine)或者布局引擎(Layout Engine)
| Chrome | Blink (c++) | V8 (c++) |
| Opera | Blink (c++) | V8 (c++) |
| Safari | Webkit (c++) | JavaScript Core (nitro) |
| FireFox | Gecko (c++) | SpiderMonkey (c/c++) |
| Edge | EdgeHTML (c++) | Chakra JavaScript Engine (c++) |
| IE | Trident (c++) | Chakra JScript Engine (c++) |
網絡(Networking) - 用于網絡調用,比如 HTTP 請求。其接口與平臺無關,并為所有平臺提供底層實現。
用戶界面后端(UI Backend) - 用于繪制基本的窗口小部件,比如組合框和窗口。其公開了與平臺無關的通用接口,而在底層使用操作系統的用戶界面方法。
JavaScript 解釋器(JavaScript Interpreter)。用于解析和執行 JavaScript 代碼,如 V8 引擎。
JS引擎線程負責解析Javascript腳本,運行代碼。
JS引擎一直等待任務隊列中任務的到來,然后加以處理,一個Tab頁(renderer進程)中只有一個JS線程在運行
數據存儲(Data Persistence)。這是持久層。瀏覽器需要在硬盤上保存各種數據,例如 Cookie。新的 HTML 規范 (HTML5) 定義了“網絡數據庫”,這是一個完整(但是輕便)的瀏覽器內數據庫。
1.3 多進程
- 瀏覽器是多進程的
- 瀏覽器之所以能夠運行,是因為系統給它的進程分配了資源(cpu、內存)
- 簡單點理解,每打開一個Tab頁,就相當于創建了一個獨立的瀏覽器進程
瀏覽器里面的進程:
Browser進程:瀏覽器的主進程(負責協調、主控),只有一個。作用有
- 負責瀏覽器界面顯示,與用戶交互。如前進,后退等
- 負責各個頁面的管理,創建和銷毀其他進程
- 將Renderer進程得到的內存中的Bitmap,繪制到用戶界面上
- 網絡資源的管理,下載等
第三方插件進程:每種類型的插件對應一個進程,僅當使用該插件時才創建
GPU進程:最多一個,用于3D繪制等
渲染進程(瀏覽器內核)(Renderer進程,內部是多線程的)
- 默認每個Tab頁面一個進程,互不影響
- 主要作用為頁面渲染,腳本執行,事件處理等
渲染進程是多線程的:
GUI渲染線程
- 負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,布局和繪制等。
- 當界面需要重繪(Repaint)或由于某種操作引發回流(reflow)時,該線程就會執行
- 注意,GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起(相當于被凍結了),GUI更新會被保存在一個隊列中等到JS引擎空閑時立即被執行。
js引擎線程
- 也稱為JS內核,負責處理Javascript腳本程序。(例如V8引擎)
- JS引擎線程負責解析Javascript腳本,運行代碼。
- JS引擎一直等待任務隊列中任務的到來,然后加以處理,一個Tab頁(renderer進程)中只有一個JS線程在運行
- 同樣注意,GUI渲染線程與JS引擎線程是互斥的。所以如果JS執行的時間過長,要放在body下面,否則就會導致頁面渲染加載阻塞。
事件觸發線程
- 管理著事件隊列
- 監聽事件,符合條件時把回調函數放入事件隊列中
定時觸發器線程
- setInterval與setTimeout在此線程中計時完畢后,把回調函數放入事件隊列中
- 瀏覽器定時計數器并不是由JavaScript引擎計數的,(因為JavaScript引擎是單線程的, 如果處于阻塞線程狀態就會影響記計時的準確),因此通過單獨線程來計時并觸發定時(計時完畢后,添加到事件隊列中,等待JS引擎空閑后執行)
- 注意,W3C在HTML標準中規定,規定要求setTimeout中低于4ms的時間間隔算為4ms。
異步http請求線程
- 檢測到XHR對象狀態變化時,將回調函數放入事件隊列中
- 將檢測到狀態變更時,如果設置有回調函數,異步線程就產生狀態變更事件,將這個回調再放入事件隊列中。再由JavaScript引擎執行。
總結一下:
- css加載不會阻塞DOM樹解析(異步加載時DOM照常構建),但會阻塞render樹渲染(渲染時需等css加載完畢,因為render樹需要css信息)
- Javascript 阻塞 DOM 解析
1.4 渲染機制
- DOM 樹與 CSSOM 樹合并后形成渲染樹
- 渲染樹只包含渲染網頁所需的節點
- 布局計算每個對象的精確位置和大小
- 最后一步是繪制,使用最終渲染樹將像素渲染到屏幕上
1.5 重排(回流)與重繪
重繪(repaint)
元素外觀的改變所觸發的瀏覽器行為,例如改變vidibility、outline、background等屬性,瀏覽器會根據元素的新屬性重新繪制,使元素呈現新的外觀,重繪不會帶來重新布局
重排(回流 reflow)
渲染樹需要重新布局,例如:在body最前面插入一個元素, dom元素的結構變化 ,調整瀏覽器窗口大小等
例子:
var bstyle = document.body.style; // cachebstyle.padding = "20px"; // reflow, repaint bstyle.border = "10px solid red"; // 再一次的 reflow 和 repaintbstyle.color = "blue"; // repaint bstyle.backgroundColor = "#fad"; // repaintbstyle.fontSize = "2em"; // reflow, repaint// new DOM element - reflow, repaint document.body.appendChild(document.createTextNode('dude!'));減少重繪與重排:
-
js盡量少訪問dom節點和css 屬性,盡量不要過多的頻繁的去增加,修改,刪除元素,因為這可能會頻繁的導致頁面reflow,可以先把該dom節點抽離到內存中進行復雜的操作然后再display到頁面上。(虛擬DOM)
-
減少不必要的 DOM 層級(DOM depth)。改變 DOM 樹中的一級會導致所有層級的改變,上至根部,下至被改變節點的子節點。這導致大量時間耗費在執行 reflow 上面。
-
不要通過父級來改變子元素樣式,最好直接改變子元素樣式,改變子元素樣式盡可能不要影響父元素和兄弟元素的大小和尺寸
-
盡量通過class來設計元素樣式,切忌用style 多次操作單個屬性
-
盡可能的為產生動畫的 HTML 元素使用 fixed 或 absolute 的 position ,那么修改他們的 CSS 是不會 Reflow 的。
-
img標簽要設置高寬,以減少重繪重排
-
避免不必要的復雜的 CSS 選擇器,尤其是后代選擇器(descendant selectors),因為為了匹配選擇器將耗費更多的 CPU
總結:
Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每個結點都會有reflow方法,一個結點的reflow很有可能導致子結點,甚至父點以及同級結點的reflow。在一些高性能的電腦上也許還沒什么,但是如果reflow發生在手機上,那么這個過程是非常痛苦和耗電的。
二、頁面加載緩慢的原因
2.1 瀏覽器部分
-
網絡層面
-
過多的HTTP請求
打開一個網頁的時候,后臺程序的響應并不所需太多時間,等待的時間主要花費在下載網頁元素上了,即HTML、CSS、JavaScript、Flash、圖片等。據統計,每增加一個元素,網頁載入的時間就會增加25-40毫秒(具體取決于用戶的帶寬情況)。
-
資源訪問帶寬小
兩方面,一方面是客戶端的帶寬,一方面是服務器端的帶寬。
-
網頁元素(圖片、視頻、樣式)太大
-
瀏覽器渲染層面
-
渲染阻塞:
瀏覽器想要渲染一個頁面就必須先構建出DOM樹與CSSOM樹,如果HTML與CSS文件結構非常龐大與復雜,這顯然會給頁面加載速度帶來嚴重影響。
所謂渲染阻塞資源,即是對該資源發送請求后還需要先構建對應的DOM樹或CSSOM樹,這種行為顯然會延遲渲染操作的開始時間。
JS阻塞與CSS阻塞:
HTML、CSS、JavaScript都是會對渲染產生阻塞的資源,HTML是必需的(沒有DOM還談何渲染),但還可以從CSS與JavaScript著手優化,盡可能地減少阻塞的產生。
-
重復渲染
-
DNS解析
-
服務端層面
- 硬件配置低:這個是雙向的
- 服務器軟件,比如防火墻、內網策略等
- 未對Nginx這類web服務器進行配置優化
- CPU占滿、數據庫未優化
- 代碼問題,代碼效率,代碼性能
- 包含了過多的分析類工具
2.2 代碼部分
-
構建層面
未對代碼進行打包、壓縮、兼容性優化。
未合并重復的請求、代碼。
-
編碼層面
沒有良好的編碼習慣,錯誤的編排JS與CSS
for循環、迭代、同步、重定向、阻塞請求
未刪除重復、無用的代碼
未對邏輯業務復雜的代碼進行重構,了解設計模式,對業務進行疏理
-
機制(SSR,英文Server Side Render:服務器端渲染)
未加入Async異步機制
未思考頁面加載、用戶體驗
-
規范
CSS規范
HTML規范/HTML5規范
Airbnb代碼規范等。
三、優化方式
3.1 減少http請求
- 合并js文件/合并css文件
- 雪碧圖的使用(css sprite)
- 使用base64(肉聯圖片)表示簡單的圖片
3.2 使用內容傳送網絡CDN
用于分發傳送內容的負載的服務器網絡。從本質上講,網站的副本存儲在多個地理位置不同的數據中心,以便用戶可以更快,更可靠地訪問網站
3.3 避免空src或者是href值
空的src和href都會導致多余的HTTP請求,雖然不影響加載時間,但是會對服務器產生不必要的流量和壓力。瀏覽器仍然會向服務器發起一個 HTTP 請求
空 src 產生請求的后果不容小憩:
- 給服務器造成意外的流量負擔,尤其時日 PV 較大時
- 浪費服務器計算資源
- 可能產生報錯
3.4 gzip 壓縮
啟用 gzip 壓縮可大幅縮減所傳輸的響應的大小(最多可縮減 90%),從而顯著縮短下載相應資源所需的時間、減少客戶端的流量消耗并加快網頁的首次呈現速度
從HTTP / 1.1開始,Web客戶端表示支持使用HTTP請求中的Accept-Encoding標頭進行壓縮
Accept-Encoding:gzip,deflate3.5 CSS放在頂部,JS放在底部
-
將內聯樣式塊和<link>元素從頁面<body>移動到頁面<head>中。
HTML 4.01規范(第12.3節)規定,始終把使用<link>標簽的外部樣式表放在<head>部分里,還要確保您指定的樣式有正確的順序。
-
把<style>區塊放在<head>部分里。
-
使用css媒體類型,讓CSS資源只在特定條件下使用
3.6 減少DNS查找
用戶訪問網站的過程如下:
在地址欄輸入網站地址,如www.example.com;
本地DNS得到這個請求,查詢本地DNS緩存,如果有這條記錄,則直接返回對應的IP;否則,請求網絡上的DNS服務器,得到相應的IP,返回給客戶機,并緩存這條記錄;
瀏覽器向得到的IP發起建立連接請求,得到響應后建立連接,請求數據;
Server端計算所需數據,并返回給client端;
client端,即瀏覽器,解析數據并顯示在瀏覽器窗口中,至此,請求完成。
縮短DNS解析的 方法可以通過延長DNS緩存的時間,選用更快的DNS Server,減少域名總數(例如原來有5個img server,分別為img1.xxx.com至img5.xxx.com,則現在可以減少到3個)等等
3.7 壓縮資源
通過對外部資源進行壓縮可以大幅度地減少瀏覽器需要下載的資源量,它會減少關鍵路徑長度與關鍵字節,使頁面的加載速度變得更快
3.8 避免3xx/4xx
3xx
重定向相關的HTTP響應代碼,意思是用戶的原始請求(例如請求A)被重定向到其他的請求(例如請求B)
每次頁面重定向到另一個頁面時,您的訪問者都會面臨等待HTTP請求 - 響應周期完成的額外時間。例如,如果移動重定向模式如下所示:
example.com - > www.example.com - > m.example.com - > m.example.com/home,這兩個額外重定向中的每一個都會使您的頁面成為可能加載速度慢。
常見的優化辦法:
-
最浪費的重定向經常發生、而且很容易被忽略:URL 末尾應該添加 / 但未添加。比如,訪問 http://astrology.yahoo.com/astrology 將被 301 重定向到 http://astrology.yahoo.com/astrology/(注意末尾的 /)。如果使用 Apache,可以通過 Alias 或 mod_rewrite 或 DirectorySlash 解決這個問題。
-
網站域名變更:CNAME 結合 Alias 或 mod_rewrite 或者其他服務器類似功能實現跳轉。
-
在定義鏈接地址的href屬性的時候,盡量使用最完整的、直接的地址。例如:
使用 www.cnblogs.com 而不是cnblogs.com
使用cn.bing.com而不是bing.com
使用www.google.com.hk而不是google.com
使用www.mysite.com/products/而不是www.mysite.com/products
避免404瀏覽器找不到資源的情況發生
發出HTTP請求并獲得無用的響應(即404 Not Found)是完全沒必要的。特別糟糕的是當外部JavaScript的鏈接錯誤并且結果是404時。首先,此下載將阻止并行下載。接下來,瀏覽器可能會嘗試解析404響應主體,就像它是JavaScript代碼一樣,這樣就帶來的性能的浪費
看不到的影響:
- 例如請求favicon.ico文件,或者請求了某個不存在的腳本文件、樣式表、圖片文件,頁面還是會按照正常的方式進行呈現。
- 丟失的腳本文件、樣式表、圖片文件,會導致頁面的某些行為、界面效果出現異常(也可能不是很明顯)
- 最大的問題可能是性能方面的影響。尤其是如果請求一個不存在的腳本文件,因為瀏覽器在請求腳本文件的時候,即便是返回404,它也會嘗試去按照Javascript的方式解析響應中的內容。這無疑會增加很多處理的時間,而因為該文件不存在,所以這些都是無用功。
看得到的影響:
- 如果用戶請求的某個頁面不存在,那么他將收到明確的回應
- 默認情況下,他將收到一個標準的錯誤頁面(請注意:不少用戶會被這個頁面嚇到)
常見的優化辦法:
404 意味著Not Found,意思是說未找到資源。既然如此,那么至少會有兩種原因導致404錯誤:
- 該資源按理說是要有,但我們沒有提供。用戶按照正常的方式來請求,所以資源找不到。
- 為網站提供favicon.ico這種經??赡軙缓雎缘馁Y源
- 使用一些檢查工具:比如Link checker
- 該資源本來就不存在,用戶按照不正常的方式來請求,當然還是找不到。
- 避免用戶收藏絕對地址,給后期更新帶來隱患。可以使用地址Rewrite來重寫,或者在設計階段定義一些靈活友好的地址
- 使用Routing技術,配置路由規則。
3.9 AJAX 優化
有的時候可能希望GET請求不被緩存,有幾種做法來達到這樣的目的:
axios中
var config = { headers: {'Content-Type': 'application/json','Cache-Control' : 'no-cache'}};axios.get('/post',config)3.10 減小 Cookie 大小
減小cookie的大小,因為在發請求時瀏覽器會將cookie信息發送到server端,所以應該只在cookie中存必要的信息且越長度越小越好。在 寫cookie的時候要記得給cookie設置一個合理的過期時間及域
3.11 利用緩存
利用瀏覽器緩存 ,為鏈接或者資源,添加Expires或Cache-Control頭
- 對于靜態組件:通過設置遠期未來Expires標頭實現“永不過期”策略
- 對于動態組件:使用適當的Cache-Control標頭來幫助瀏覽器處理條件請求
3.12 縮短服務器響應時間
總結
以上是生活随笔為你收集整理的浏览器原理及性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我给同事配的实用型的家庭多媒体影院系统
- 下一篇: 初中信息技术计算机基础说课稿,初中信息技