參考:https://www.cnblogs.com/caiyy/p/10406934.html
GUI和Web軟件
1、GUI是通過 代碼打包下載 安裝到如手機上進行訪問的過程
2、Web端的軟件是通過 發(fā)布到server或cdn上,網(wǎng)頁通過增量式訪問獲取服務的
瀏覽器多進程
包含一個主進程(包含網(wǎng)絡渲染,頁面管理),插件進程,GPU(3d)渲染進程(可選),頁面渲染進程(js處理,頁面渲染,事件處理)
渲染進程:
1、broswer主進程 進行瀏覽器的頁面資源管理 創(chuàng)建和銷毀其他進程,頁面交互,將位圖合并(布局渲染樹)繪制到頁面上
2、GPU進程,硬件加速 只有一個
3、插件進程 每個類型的插件對應一個進程
4、渲染進程 每個tab頁一個進程,負責頁面渲染 腳本執(zhí)行 事件觸發(fā),任務隊列輪詢等
——JS引擎線程(V8引擎):負責js腳本解析,代碼運行 與GUI互斥
——GUI渲染線程: 解析html和css,負責布局和繪制,與js引擎線程互斥,這是由于js可以操作dom,防止渲染前后不一致
——事件觸發(fā)線程:歸屬于瀏覽器,用于控制事件循環(huán),當事件被觸發(fā)該線程會將對應的回調(diào)函數(shù)放到任務隊列當中
——定時觸發(fā)器線程:setTimeout setTnterval W3C中規(guī)定setTimeout低于4ms算4ms 回調(diào)函數(shù)放入任務隊列
——異步http請求線程:監(jiān)測狀態(tài)變更時產(chǎn)生狀態(tài)變更事件,放入任務隊列
——任務隊列輪詢線程:輪詢監(jiān)聽任務隊列是否為空
webWorker和shareWorker
js是單線程的 當有大量運算會造成頁面渲染卡頓,為了避免可以申請一個web worker。 js引擎向瀏覽器申請開一個子線程(不可造作dom),js引擎線程和worker線程通過特定的方式通信,計算出結果后通信給js引擎主線程。
webWorker是某個頁面render下的一個線程,不會和其他頁面的render進程共享
shareWorder是瀏覽器所有頁面共享的,是一個單獨的進程
GUI渲染
網(wǎng)頁從白屏到內(nèi)容顯示的時間就是HTML 文檔加載和解析的時間。也就是DOMContentLoaded 事件觸發(fā)之前所經(jīng)歷的時間。
js腳本參數(shù)設置: https://blog.csdn.net/zyj0209/article/details/79698430
由js引擎來解析html生成dom樹,css解析生成cssdom樹,js腳本執(zhí)行可以設置參數(shù)默認是
1、同步sync,當執(zhí)行html的過程中遇到js腳本,會停止解析html,先去加載執(zhí)行js腳本完畢后繼續(xù)解析html
2、異步async,當執(zhí)行html遇到js,會同時下載js腳本,如果js先加載完就停止解析html先執(zhí)行js腳本,然后解析html或者是這個時候html已經(jīng)加載完畢,那么直接執(zhí)行js,不管是哪一種,DOMContentLoaded都會在html解析完觸發(fā)
3、延后dsync,遇到js腳本會進行下載,腳本需要等待html解析完才會執(zhí)行,DOMContentLoaded會在js腳本執(zhí)行完了被觸發(fā)
dom樹和cssdom樹結合為渲染樹render-tree,從根節(jié)點遞歸調(diào)用計算每一個元素的大小,位置等,給出每個節(jié)點在屏幕上精確的坐標,這就是基于渲染樹的布局渲染樹,之后交給主進程進行渲染樹的繪制。
資源阻塞機制
DOMContentLoaded和load分別標識著dom加載完成和dom&&css&&js加載完成
GUI線程中html和css解析是并行的,css不會阻塞html的解析但是會阻塞頁面渲染,所有放在head中盡量早解析
js根據(jù)需要不同可以放在不同的位置,初始化放在head,操作dom放在body末尾或者使用load事件
回流和重繪
render樹中的部分或全部因為元素的尺寸、布局、隱藏等改變需要重新構建稱為回流,如
1、頁面初始化、調(diào)整窗口,改變字體,內(nèi)容變化、操作dom、操作css、激活css偽類、操作class屬性、設置style屬性、增加或者移除樣式表
2、如何防止回流:減少逐項修改style最好一次定義在class中更新、避免循環(huán)操作dom、到要頻繁的獲取offset屬性時,不重復讀取,將復雜的元素絕對定位或者固定定位,使用GPU硬件加速創(chuàng)建一個新的復合圖層
圖層:dom中的每個節(jié)點對應一個簡單圖層,復合圖層是對簡單圖層的合并,absolute,fixed布局會脫離文檔流,但是還在當前的復合圖層中,會影響重繪,但是回流不影響。
當使用硬件加速的時候會生成新的復合圖層,互不影響,設置transform: translate3d(0,0,0) 或 translateZ(0),更多內(nèi)容參考:http://www.itdecent.cn/p/f8b1d6e598db
proload和prefetch
詳情:https://www.cnblogs.com/xiaohuochai/p/9183874.html
proload提升資源的優(yōu)先級,提升到跟設置了as屬性的同一優(yōu)先級,as屬性設置資源的優(yōu)先級,不設置會默認為異步請求的優(yōu)先級,很低。不管資源是否需要都會被加載,并且有回調(diào)函數(shù)。設置方式<link rel="preload" href="..." as="..." onload="preloadFinished()">
prefetch是預先加載下一頁可能用到的資源,不可混用。
事件循環(huán)機制
更多:https://juejin.im/post/5ec73026f265da76da29cb25
- 事件循環(huán)機制的核心是事件觸發(fā)線程,js執(zhí)行棧的過程中觸發(fā)異步任務,或者是微任務,將異步任務交給相關的線程,將微任務放到微任務隊列;
- 當執(zhí)行棧執(zhí)行完畢后去查看微任務,執(zhí)行當次產(chǎn)生的微任務,這個時候如果有微任務添加進來會繼續(xù)執(zhí)行微任務直到js執(zhí)行棧為空才去渲染;
- 同時異步任務完成后會將回調(diào)函數(shù)放到任務隊列(宏任務)當中,當執(zhí)行棧結束&&微任務結束,進入渲染階段
1、判斷是否需要重渲染,一般涉及到屏幕刷新率、頁面性能、程序是否在后臺執(zhí)行,一般屏幕刷新率為60Hz,,如果頁面性能過低,為了保證穩(wěn)定的刷新率會選擇30Hz,當在一次刷新幀內(nèi)發(fā)生多次動畫行為,并不會真實的渲染到頁面,而會被瀏覽器收集起來一次執(zhí)行;如果程序在后臺執(zhí)行會降低刷新率到4hz甚至更低;如果瀏覽器認為渲染不會引起頁面視覺上的變化;如果幀動畫回調(diào)函數(shù)為空(可以通過requestAnimationFrame函數(shù)觸發(fā));滿足以上條件不進行渲染
2、如果判斷需要渲染則進行渲染否則直接開始進行后續(xù)代碼的執(zhí)行,也是在這里進行resize scroll的觸發(fā),這里瀏覽器會保存一個,目標對象,等到這里派發(fā)事件到目標上的時候驅(qū)動目標對象的回調(diào)函數(shù)。觸發(fā)幀動畫回調(diào)(requAnimationFrame);執(zhí)行IntersectionObserver回調(diào);繪制頁面;判斷宏任務和微任務隊列為空,執(zhí)行空閑周期算法判斷是否要執(zhí)行requestIdleCallback的回調(diào)函數(shù) - 開啟下一輪的事件循環(huán),從宏任務隊列獲取新的任務放到執(zhí)行棧執(zhí)行
宏任務 macro-task:script整體代碼、setTimeout、setInterval、setImmediate、I/O、UIrendering
微任務 micro-task:process、nextTick、Promises、Object.observe、MutationObserver
注:
- async await 最終的結果是返回了promise,在await后跟隨的語句是同步的,下一行開始的語句是異步的 等同于then后面的微任務回調(diào)
- 對于任務隊列中的任務,并不是只有一個,對于用戶輸入的任務如鼠標鍵盤事件將會優(yōu)先于其他的task,瀏覽器在保證順序的前提下將會分配給用戶事件4/3的優(yōu)先權
- requestAnimationFrame函數(shù)是在頁面重新渲染之前的最后一步調(diào)用,很可能在宏任務之后不進行調(diào)用
- requestIdleCallback(fn, deadline)函數(shù)是瀏覽器提供的空閑調(diào)度算法,將一些計算量大而且又不緊急的任務放到空閑時間去執(zhí)行,每次使用的時候要去調(diào)用timeRemaining()函數(shù)獲取deadline來判斷是否有空余時間可以使用,如果有更高優(yōu)先級的任務出現(xiàn)則將在沒有渲染任務的時候則會動態(tài)的將剩余時間設置為0;需要注意的是每次當瀏覽器是空閑的也會有deadline為50ms這是為了應對用戶的交互操作發(fā)生時,確保用戶在無感知的延遲下得到回應
例題:https://blog.csdn.net/weixin_34176694/article/details/91400057
瀏覽器渲染之前做了什么
更多:https://juejin.im/post/5e6394b4e51d4526e32c3cef
需要注意的是,每次渲染的的條件是js的執(zhí)行棧為空,也就是觸發(fā)了requestAnimationFrame()方法,所以在執(zhí)行代碼的時候,如
document.addElementListener('click', function(){console.log(1, promise.resolve().then(() =>{console.log(2)}))})
document.addElementListener('click', function(){console.log(3, promise.resolve().then(() =>{console.log(4)}))})
在代碼中調(diào)用,會在js執(zhí)行棧中加載一個腳本文件,第一個監(jiān)聽器觸發(fā)后,腳本文件scripts還在所以這個時候不能去執(zhí)行微任務,還是繼續(xù)執(zhí)行宏任務,執(zhí)行完后退出js堆棧,這時才去清空微任務,結果:1=>3=>2=>4
如果是在頁面上點擊觸發(fā),沒有初始的js腳本,則會在第一個監(jiān)聽觸發(fā)后判斷堆棧為空去執(zhí)行微任務,結果為1=>2=>3=>4

這里有一個圖,三個環(huán),中間是事件循環(huán),左邊是其他的事件(宏任務、微任務),右邊是瀏覽器渲染,在左邊完成后,會按照順序推到中間的js執(zhí)行棧中,在js執(zhí)行棧為空的時候觸發(fā)requestAnimationFrame()去進行頁面渲染,如下代碼,微任務執(zhí)行后繼續(xù)添加了新的任務到js棧中,會循環(huán)執(zhí)行
function loop(){promise.resolve().then(loop)}
loop()
setTimeout(() => {
console.log("sto")
requestAnimationFrame(() => console.log("rAF"))
})
setTimeout(() => {
console.log("sto")
requestAnimationFrame(() => console.log("rAF"))
})
如果用setTimeout循環(huán)來進行動畫的循環(huán)處理,會比requestAnimationFrame()執(zhí)行的次數(shù)多,這是因為瀏覽器一般為60Hz也就是一秒最多渲染60次,這里是由于人眼的感知所決定,60Hz已經(jīng)是較為流暢的實現(xiàn),當然與硬件顯卡之類的設備也有關系,硬件決定上限,性能平衡決定結果;所以setTimeout最多可以達到瀏覽器的閾值,當然多余的調(diào)用就被浪費了,同時可能會因為在每一個動畫幀調(diào)用次數(shù)不同而出現(xiàn)漂移,也可能因為在沒有匹配到動畫幀而丟失渲染,不可控,如上面的代碼第二段,瀏覽器期待在定時器之間不穿插渲染,所以會將兩次渲染合并到一起,結果是:sto sto rAF rAF
而requestAnimationFrame是在每一次渲染之前調(diào)用,更加科學的用于動畫的處理,有些人會把requestAnimationFrame方法歸類為和setTimeout一樣都屬于宏任務隊列,但是實際上來說與宏任務和微任務無關它只和瀏覽器渲染相關,在瀏覽器刷新前執(zhí)行