大家好,我是前端嵐楓,兩個多月沒更新博客了,這段時間在忙著找工作,入職新公司,各項都有序的進行著,過年的一年算是很特殊的一年,經歷7.20暴雨,8月疫情在家辦公一個月,及后面的疫情,12月開始原公司長期放假,這些種種,真是一言難盡,但是進入2022年有個好的開始,1月入職新公司,年會抽獎中獎,緊接著沒有被疫情留在鄭州,順利回家過年。新的一年開始,我的技術博文更新也會持續更新。今天主要跟大家分享我整理的瀏覽器渲染原理及性能優化:性能優化是我們工作中常遇到一些問題, 也是面試官經常提問的問題,希望下面文章對大家有所幫助。
一、進程與線程
- 進程是操作系統資源分配的基本單位,進程中包含線程
- 線程是由進程所管理的,為了提升瀏覽器的穩定性和安全性,瀏覽器采用了多進程模型

- 瀏覽器進程:負責界面顯示、用戶交互、子進程管理、提供存儲等
- 渲染進程: 每個頁面都有單獨的渲染進程,核心用于渲染頁面
- 網絡進程:主要處理網絡資源加載(html、css、js 等)
- GPU進程:3d繪制,提高性能
- 插件進程:chrome中安裝的一些插件
二、從輸入URL到瀏覽器顯示頁面發生了什么
用戶輸入的關鍵字還是URL?如果是關鍵字則使用默認搜索引擎生成URL
- 用戶輸入url地址(關鍵字 會將關鍵字根據默認引擎生成地址)會開始導航,瀏覽器進程里面做
- 瀏覽器進程 :會準備一個渲染進程用戶渲染頁面
- 網絡進程加載資源,最終將加載的資源交給渲染進程來處理
- 渲染完畢顯示 ipc
網絡七層模型
- 先去查找緩存,檢測緩存是否過期,直接返回緩存中的內容
- 看域名是否被解析,DNS協議,將域名解析成ip地址(DNS基于UDP)ip+端口號 host
- 請求是https SSL協商
- ip地址來進行尋地址,排隊等待,最多能發送6個http請求
- tcp 創建鏈接,用于傳輸(三次握手)
- 利用tcp傳輸數據(拆分成數據包 有序)可靠、有序,服務器按照順序來接受
- http 請求(請求行 請求頭 請求體)
- 默認不會斷開keep-alive, 為了下次傳輸數據時候可以復用上次的創建的鏈接
- 服務器收到數據后(響應行 響應頭 響應體)
- 服務器返回301 302 會進行重定向操作
- 服務器返回304 去查詢瀏覽器緩存進行返回
http 0.9 負責傳輸html 最早的時候沒有請求頭和響應頭
http 1.0 提供了 http的header 根據header 的不同來處理不同的資源
http 1.1 默認開啟了keep-alive 鏈接復用 管線化 服務器處理多個請求(隊頭阻塞問題)
http 2.0 用同一個tcp鏈接來發送數據 一個域名一個tcp(多路復用)頭部壓縮 服務器可以推送數據給服務端
http3.0 解決了tcp的隊頭阻塞問題 QUIC協議 采用了udp
渲染流程

- 瀏覽器無法直接使用HTML,需要將HTML轉化成DOM樹。(document)
- 瀏覽器無法解析純文本的css樣式,需要對css進行解析成styleSheets.(document.styleSheets)
- 計算出DOM樹中的每個節點的具體樣式
- 創建渲染(布局)樹,將DOM樹中可見節點,添加到布局樹中,并計算節點渲染到頁面的坐標位置,(layout)
- 通過布局樹,進行分層(根據定位屬性,transform屬性,clip屬性等)生產圖層樹
- 將不同圖層進行繪制,轉交給合成線程處理,最終生產頁面,并顯示到瀏覽器上(Painting Display)
查看layer并對圖層進行繪制的列表
- css 不會阻塞html解析, 先解析html,再解析css, 樣式
- css 放到底部,可能會導致重繪效果, 當html 渲染時候,會先掃描js 和css 渲染從上到下,邊解析邊渲染
- 渲染DOM時,要等待樣式加載完畢
- js會阻塞html解析,阻止DOM渲染 需要暫停DOM解析去執行js ,js可能會操作樣式,所以需要等待樣式加載完成
所以js 一般放頁面底部,css 放頭部
總結:DOM 如何生成
- 在解析前會先執行預解析操作,會預先加載js、css等文件
- 字節流 =》分詞器 =》Token =》根據token 生成節點 =》插入到DOM 樹中
- 遇到js 執行過程中遇到script 標簽,HTMLParse會停止解析,(下載)執行對應的腳本
- 在js 執行前,需要等待當前腳本之上的所有css 加載解析完畢(js是依賴css的加載)#
三、Perfomance API


// js
window.addEventListener('DOMContentLoaded', function(){
let s = 0;
for(let i = 0; i< 10000000; i++){
s+=i;
}
setTimeout(()=>{
document.body.appendChild(document.createtextNode('hello'))
},1000)
})
setTimeout(()=>{
const {
fetchStart, //開始訪問
requestStart, //請求的開始
responseStart, //響應的開始
responseEnd, //響應的結束
domInteractive, // dom 可交互的時間點
domContentLoadedEventEnd, //dom加載完畢
loadEventStart //所有資源加載完畢
} = performance.timing;
let TTFB = responseStart - requestStart; //首字節返回的事件 服務器處理能力
let TTI = domInteractive - fetchStart; //整個的一個可交互的時間
let DCL= domContentLoadedEventEnd - fetchStart; //DOM整個加載完畢
console.log(TTFB, TTI, DCL) //如圖一
const paint = performance.getEnteriesByType('paint')
console.log(paint[0].startTime); //FP
}, 3000)
//遞歸看load 的時間不能為0 mutationObserver
new PerformanceObserver((entryList)=>{
console.log(entryList.getEntries)
}).observe({entryTypes:['element']})
圖一

圖二

四、網絡優化策略
- 減少HTTP請求數,合并js、css,合理內嵌js、css
- 合理設置服務器端緩存,提高服務器處理速度。(強制緩存、協商緩存)
- 避免重定向,重定向會降低響應速度(301,302)
- 使用dns-prefetch,進行DNS預解析
- 采用域名分片技術,講資源放到不同的域名下,同一個域名最多處理6個TCP鏈接問題
- 采用CDN加速加快訪問速度。(指派最近、高度可用)
- gzip壓縮優化,對傳輸資源進行體積壓縮(html,css,js)
Content-Encoding:gzip
- 加載數據優先級:preload(預先請求當前頁面需要的資源) prefetch(將來頁面中使用的資源),將數據緩存到HTTP緩存中
<link rel="preload" href="style.css" as="style">
五、關鍵渲染路徑

- 重排(回流)Reflow:添加元素、刪除元素、修改大小、移動元素位置、獲取位置等相關信息都會引起重排。
- 重繪 Repaint: 頁面中元素樣式的改變并不影響它在文檔中的位置。
1.強制同步布局問題
JavaScript強制將計算樣式和布局操作提前到當前任務中
<div id="app"></div>
<script>
function reflow(){
let el = documnet.getElementById('app');
let node = doucument.createElement('h1');
node.innerHTML = 'hello';
el.appendChild(node);
//獲取位置會導致重排(重新布局)
console.log(app.offsetHeight);
}
window.addEventListener('load',function(){
for(let i=0; i<100; i++){
reflow()
}
})
</scpript>
減少回流和重繪
- 脫離文檔流
- 渲染時給圖片增加固定寬高
- 盡量使用css3動畫
- 可以使用will-change提取到單獨的圖層中
六、靜態文件優化
1. 圖片優化
圖片格式:
- jpg:適合色彩豐富的照片、banner圖;不適合圖形文字、圖標(紋理有鋸齒),不支持透明
- png: 適合純色、透明、圖標、支持半透明;不適合色彩豐富圖片,因為無損儲存會導致儲存體積大
- gif: 適合動畫,可以動的圖標;不支持半透明,不適合存儲彩色圖片
- webp: 適合半透明圖片,可以保證圖片質量和較小的體積
- svg: 相比于jpg和png,它的體積更小,渲染成本過高,適合小且色彩單一的圖標
圖片優化:
- 避免空src的圖片
- 減小圖片尺寸,節約用戶流量
- img標簽設置alt屬性,提升圖片加載失敗時的用戶體驗
- 原生的loading:lazy圖片懶加載
<img loading="lazy" src="./images/1.jpg" widht="300" height="450" />
- 不同環境下,加載不同尺寸和像素的圖片
- 對于較大的圖片可以考慮采用漸進式圖片
- 采用base64URL 減少圖片請求
- 采用雪碧圖片合并圖標圖片
2. HTML優化
- 語義化HTML:代碼簡潔清晰,利于搜索引擎,便于團隊開發維護
- 提前聲明字符編碼,讓瀏覽器快速確定如何渲染網頁內容
- 減少HTML嵌套關系,減少DOM節點數量
- 刪除多余空格,空行、注釋、及無用的屬性等
- 避免table布局
3. css 優化
- 減少偽類選擇器,減少樣式層數,減少使用通配符
- 避免使用css表達式,css表達式會頻繁求值,當滾動頁面,或者移動鼠標時,都會重新計算(IE6,7)
background-color:expression(new Date()).getHours()%2 ? "red" : "yellow");
- 刪除空行、注釋、減少無意義的單位、css進行壓縮
- 使用外鏈css,可以對css進行緩存
- 添加媒體字段,只加載有效的css 文件
<link href="index.css" rel="stylesheet" media="screen and(min-width:1024px)"
- css contain屬性將元素進行隔離
- 減少@import使用,由于import采用的串行加載
4. js 優化
- 通過async 、defer異步加載文件

- 減少DOM操作,緩存訪問過的元素
- 操作不直接應用到DOM上,而應用到虛擬DOM上。最后一次性的應用到DOM上
- 使用webworker解決程序阻塞問題
- IntersectionOberver
const observer = new IntersectionOberver(function(changes){
changes.forEach(function(element, index){
if(element,intersectionRatio > 0){
observer.unobserve(element.target);
element.target.src = element.target,dataset.src;
}
});
});
function initObserve(){
const listItems = document.querySelectAll('img');
listItems.forEach(function(item){
observer.observe(item)
})
}
initObserver
- 虛擬滾動 vertual-scroll-list
- requestAnimationFrame、requestIdleCallback

- 盡量避免使用eval,消耗時間久
- 使用事件委托,減少事件綁定個數
- 盡量使用canvas動畫、css動畫
七、優化策略
- 關鍵資源個數越多,,首次頁面加載時間就會越長
- 關鍵資源的大小,內容越小,下載時間越短
- 優化白屏:內聯css和內聯js,移除文件下載較小文件體積
- 預渲染,打包時候進行預渲染
- 使用SSR加速首屏加載(耗費服務端資源),有利于SEO優化,首屏利用服務器端渲染,后續交互采用客戶端渲染
八、瀏覽器的存儲
-
cookie: cookie過期時間內一直有效,存儲大小4k左右、同時限制字段個數,不適合大量的數據存儲,每次請求會攜帶cookie,主要可以利用做身份檢查驗證
設置cookie有效期;根據不同子域劃分cookie較少傳輸;靜態資源域名和cookie域名采用不同域名,避免靜態資源訪問時攜帶cookie
localStroage: chrome下最大存儲5M,除非手動清除,否則一直存在,利用localStorage存儲靜態資源
function cacheFile(url){
let fileContent = localStorage.getItem()
if(file){
eval(fileContent)
} else {
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function(){
let responseText = xhr.responseText;
eval(responseText);
localStorage.setItem(url,responseText)
}
xhr.send()
}
}
cacheFile('/index.js')
- sessionStorage: 會話級別存儲,可用于頁面間的傳值
- indexDB:瀏覽器的本地數據庫(基本無上限)
九、增加體驗 PWA(Progerssive Web App)
webapp用戶體驗差(不能離線訪問),用戶粘性低(無法保存入口),pwa就是為了解決這一系列問題讓webapp具有快速,可靠,安全等特點
- Web App Mainfset: 將網站添加到桌面、更類似native的體驗
- Service Worker: 離線緩存內容,配合cache API
- Push Api & Notification Api: 消息推送與提醒
- App Shell & App Skeleton App殼、骨架屏