參考:https://www.cnblogs.com/caiyy/p/10406934.html
GUI和Web軟件
1台盯、GUI是通過 代碼打包下載 安裝到如手機(jī)上進(jìn)行訪問的過程
2沥割、Web端的軟件是通過 發(fā)布到server或cdn上废岂,網(wǎng)頁通過增量式訪問獲取服務(wù)的
瀏覽器多進(jìn)程
包含一個主進(jìn)程(包含網(wǎng)絡(luò)渲染,頁面管理)敬锐,插件進(jìn)程绳慎,GPU(3d)渲染進(jìn)程(可選)丁存,頁面渲染進(jìn)程(js處理,頁面渲染墓造,事件處理)
渲染進(jìn)程:
1堪伍、broswer主進(jìn)程 進(jìn)行瀏覽器的頁面資源管理 創(chuàng)建和銷毀其他進(jìn)程,頁面交互觅闽,將位圖合并(布局渲染樹)繪制到頁面上
2帝雇、GPU進(jìn)程,硬件加速 只有一個
3蛉拙、插件進(jìn)程 每個類型的插件對應(yīng)一個進(jìn)程
4尸闸、渲染進(jìn)程 每個tab頁一個進(jìn)程,負(fù)責(zé)頁面渲染 腳本執(zhí)行 事件觸發(fā)孕锄,任務(wù)隊列輪詢等
——JS引擎線程(V8引擎):負(fù)責(zé)js腳本解析吮廉,代碼運(yùn)行 與GUI互斥
——GUI渲染線程: 解析html和css,負(fù)責(zé)布局和繪制畸肆,與js引擎線程互斥宦芦,這是由于js可以操作dom,防止渲染前后不一致
——事件觸發(fā)線程:歸屬于瀏覽器轴脐,用于控制事件循環(huán)调卑,當(dāng)事件被觸發(fā)該線程會將對應(yīng)的回調(diào)函數(shù)放到任務(wù)隊列當(dāng)中
——定時觸發(fā)器線程:setTimeout setTnterval W3C中規(guī)定setTimeout低于4ms算4ms 回調(diào)函數(shù)放入任務(wù)隊列
——異步http請求線程:監(jiān)測狀態(tài)變更時產(chǎn)生狀態(tài)變更事件,放入任務(wù)隊列
——任務(wù)隊列輪詢線程:輪詢監(jiān)聽任務(wù)隊列是否為空
webWorker和shareWorker
js是單線程的 當(dāng)有大量運(yùn)算會造成頁面渲染卡頓豁辉,為了避免可以申請一個web worker令野。 js引擎向瀏覽器申請開一個子線程(不可造作dom),js引擎線程和worker線程通過特定的方式通信徽级,計算出結(jié)果后通信給js引擎主線程气破。
webWorker是某個頁面render下的一個線程,不會和其他頁面的render進(jìn)程共享
shareWorder是瀏覽器所有頁面共享的餐抢,是一個單獨(dú)的進(jìn)程
GUI渲染
網(wǎng)頁從白屏到內(nèi)容顯示的時間就是HTML 文檔加載和解析的時間现使。也就是DOMContentLoaded 事件觸發(fā)之前所經(jīng)歷的時間。
js腳本參數(shù)設(shè)置: https://blog.csdn.net/zyj0209/article/details/79698430
由js引擎來解析html生成dom樹旷痕,css解析生成cssdom樹碳锈,js腳本執(zhí)行可以設(shè)置參數(shù)默認(rèn)是
1、同步sync欺抗,當(dāng)執(zhí)行html的過程中遇到j(luò)s腳本售碳,會停止解析html,先去加載執(zhí)行js腳本完畢后繼續(xù)解析html
2、異步async贸人,當(dāng)執(zhí)行html遇到j(luò)s间景,會同時下載js腳本,如果js先加載完就停止解析html先執(zhí)行js腳本艺智,然后解析html或者是這個時候html已經(jīng)加載完畢倘要,那么直接執(zhí)行js,不管是哪一種十拣,DOMContentLoaded都會在html解析完觸發(fā)
3封拧、延后dsync,遇到j(luò)s腳本會進(jìn)行下載夭问,腳本需要等待html解析完才會執(zhí)行泽西,DOMContentLoaded會在js腳本執(zhí)行完了被觸發(fā)
dom樹和cssdom樹結(jié)合為渲染樹render-tree释涛,從根節(jié)點遞歸調(diào)用計算每一個元素的大小凶硅,位置等,給出每個節(jié)點在屏幕上精確的坐標(biāo)世剖,這就是基于渲染樹的布局渲染樹埠胖,之后交給主進(jìn)程進(jìn)行渲染樹的繪制糠溜。
資源阻塞機(jī)制
DOMContentLoaded和load分別標(biāo)識著dom加載完成和dom&&css&&js加載完成
GUI線程中html和css解析是并行的,css不會阻塞html的解析但是會阻塞頁面渲染直撤,所有放在head中盡量早解析
js根據(jù)需要不同可以放在不同的位置非竿,初始化放在head,操作dom放在body末尾或者使用load事件
回流和重繪
render樹中的部分或全部因為元素的尺寸谋竖、布局红柱、隱藏等改變需要重新構(gòu)建稱為回流,如
1蓖乘、頁面初始化锤悄、調(diào)整窗口,改變字體嘉抒,內(nèi)容變化零聚、操作dom、操作css些侍、激活css偽類隶症、操作class屬性、設(shè)置style屬性岗宣、增加或者移除樣式表
2蚂会、如何防止回流:減少逐項修改style最好一次定義在class中更新、避免循環(huán)操作dom耗式、到要頻繁的獲取offset屬性時胁住,不重復(fù)讀取趁猴,將復(fù)雜的元素絕對定位或者固定定位,使用GPU硬件加速創(chuàng)建一個新的復(fù)合圖層
圖層:dom中的每個節(jié)點對應(yīng)一個簡單圖層措嵌,復(fù)合圖層是對簡單圖層的合并躲叼,absolute,fixed布局會脫離文檔流企巢,但是還在當(dāng)前的復(fù)合圖層中,會影響重繪让蕾,但是回流不影響浪规。
當(dāng)使用硬件加速的時候會生成新的復(fù)合圖層,互不影響探孝,設(shè)置transform: translate3d(0,0,0) 或 translateZ(0)笋婿,更多內(nèi)容參考:http://www.reibang.com/p/f8b1d6e598db
proload和prefetch
詳情:https://www.cnblogs.com/xiaohuochai/p/9183874.html
proload提升資源的優(yōu)先級,提升到跟設(shè)置了as屬性的同一優(yōu)先級顿颅,as屬性設(shè)置資源的優(yōu)先級缸濒,不設(shè)置會默認(rèn)為異步請求的優(yōu)先級,很低粱腻。不管資源是否需要都會被加載庇配,并且有回調(diào)函數(shù)。設(shè)置方式<link rel="preload" href="..." as="..." onload="preloadFinished()">
prefetch是預(yù)先加載下一頁可能用到的資源绍些,不可混用捞慌。
事件循環(huán)機(jī)制
更多:https://juejin.im/post/5ec73026f265da76da29cb25
- 事件循環(huán)機(jī)制的核心是事件觸發(fā)線程,js執(zhí)行棧的過程中觸發(fā)異步任務(wù)柬批,或者是微任務(wù)啸澡,將異步任務(wù)交給相關(guān)的線程,將微任務(wù)放到微任務(wù)隊列氮帐;
- 當(dāng)執(zhí)行棧執(zhí)行完畢后去查看微任務(wù)嗅虏,執(zhí)行當(dāng)次產(chǎn)生的微任務(wù),這個時候如果有微任務(wù)添加進(jìn)來會繼續(xù)執(zhí)行微任務(wù)直到j(luò)s執(zhí)行棧為空才去渲染上沐;
- 同時異步任務(wù)完成后會將回調(diào)函數(shù)放到任務(wù)隊列(宏任務(wù))當(dāng)中皮服,當(dāng)執(zhí)行棧結(jié)束&&微任務(wù)結(jié)束,進(jìn)入渲染階段
1奄容、判斷是否需要重渲染冰更,一般涉及到屏幕刷新率、頁面性能昂勒、程序是否在后臺執(zhí)行蜀细,一般屏幕刷新率為60Hz,戈盈,如果頁面性能過低奠衔,為了保證穩(wěn)定的刷新率會選擇30Hz谆刨,當(dāng)在一次刷新幀內(nèi)發(fā)生多次動畫行為,并不會真實的渲染到頁面归斤,而會被瀏覽器收集起來一次執(zhí)行痊夭;如果程序在后臺執(zhí)行會降低刷新率到4hz甚至更低;如果瀏覽器認(rèn)為渲染不會引起頁面視覺上的變化脏里;如果幀動畫回調(diào)函數(shù)為空(可以通過requestAnimationFrame函數(shù)觸發(fā))她我;滿足以上條件不進(jìn)行渲染
2、如果判斷需要渲染則進(jìn)行渲染否則直接開始進(jìn)行后續(xù)代碼的執(zhí)行迫横,也是在這里進(jìn)行resize scroll的觸發(fā)番舆,這里瀏覽器會保存一個,目標(biāo)對象矾踱,等到這里派發(fā)事件到目標(biāo)上的時候驅(qū)動目標(biāo)對象的回調(diào)函數(shù)恨狈。觸發(fā)幀動畫回調(diào)(requAnimationFrame);執(zhí)行IntersectionObserver回調(diào)呛讲;繪制頁面
禾怠;判斷宏任務(wù)和微任務(wù)隊列為空,執(zhí)行空閑周期算法判斷是否要執(zhí)行requestIdleCallback的回調(diào)函數(shù) - 開啟下一輪的事件循環(huán)贝搁,從宏任務(wù)隊列獲取新的任務(wù)放到執(zhí)行棧執(zhí)行
宏任務(wù) macro-task:script整體代碼吗氏、setTimeout、setInterval徘公、setImmediate牲证、I/O、UIrendering
微任務(wù) micro-task:process关面、nextTick坦袍、Promises、Object.observe等太、MutationObserver
注:
- async await 最終的結(jié)果是返回了promise捂齐,在await后跟隨的語句是同步的,下一行開始的語句是異步的 等同于then后面的微任務(wù)回調(diào)
- 對于任務(wù)隊列中的任務(wù)缩抡,并不是只有一個奠宜,對于用戶輸入的任務(wù)如鼠標(biāo)鍵盤事件將會優(yōu)先于其他的task,瀏覽器在保證順序的前提下將會分配給用戶事件4/3的優(yōu)先權(quán)
- requestAnimationFrame函數(shù)是在頁面重新渲染之前的最后一步調(diào)用瞻想,很可能在宏任務(wù)之后不進(jìn)行調(diào)用
- requestIdleCallback(fn, deadline)函數(shù)是瀏覽器提供的空閑調(diào)度算法压真,將一些計算量大而且又不緊急的任務(wù)放到空閑時間去執(zhí)行,每次使用的時候要去調(diào)用timeRemaining()函數(shù)獲取deadline來判斷是否有空余時間可以使用蘑险,如果有更高優(yōu)先級的任務(wù)出現(xiàn)則將在沒有渲染任務(wù)的時候則會動態(tài)的將剩余時間設(shè)置為0滴肿;需要注意的是每次當(dāng)瀏覽器是空閑的也會有deadline為50ms這是為了應(yīng)對用戶的交互操作發(fā)生時,確保用戶在無感知的延遲下得到回應(yīng)
例題: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í)行微任務(wù)吼肥,還是繼續(xù)執(zhí)行宏任務(wù)录平,執(zhí)行完后退出js堆棧,這時才去清空微任務(wù)缀皱,結(jié)果:1=>3=>2=>4
如果是在頁面上點擊觸發(fā)萄涯,沒有初始的js腳本,則會在第一個監(jiān)聽觸發(fā)后判斷堆棧為空去執(zhí)行微任務(wù)唆鸡,結(jié)果為1=>2=>3=>4
這里有一個圖,三個環(huán)枣察,中間是事件循環(huán)争占,左邊是其他的事件(宏任務(wù)、微任務(wù))序目,右邊是瀏覽器渲染臂痕,在左邊完成后,會按照順序推到中間的js執(zhí)行棧中猿涨,在js執(zhí)行棧為空的時候觸發(fā)requestAnimationFrame()去進(jìn)行頁面渲染握童,如下代碼,微任務(wù)執(zhí)行后繼續(xù)添加了新的任務(wù)到j(luò)s棧中叛赚,會循環(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)來進(jìn)行動畫的循環(huán)處理澡绩,會比requestAnimationFrame()執(zhí)行的次數(shù)多,這是因為瀏覽器一般為60Hz也就是一秒最多渲染60次俺附,這里是由于人眼的感知所決定肥卡,60Hz已經(jīng)是較為流暢的實現(xiàn),當(dāng)然與硬件顯卡之類的設(shè)備也有關(guān)系事镣,硬件決定上限步鉴,性能平衡決定結(jié)果;所以setTimeout最多可以達(dá)到瀏覽器的閾值璃哟,當(dāng)然多余的調(diào)用就被浪費(fèi)了氛琢,同時可能會因為在每一個動畫幀調(diào)用次數(shù)不同而出現(xiàn)漂移,也可能因為在沒有匹配到動畫幀而丟失渲染随闪,不可控阳似,如上面的代碼第二段,瀏覽器期待在定時器之間不穿插渲染蕴掏,所以會將兩次渲染合并到一起障般,結(jié)果是:sto sto rAF rAF
而requestAnimationFrame是在每一次渲染之前調(diào)用调鲸,更加科學(xué)的用于動畫的處理,有些人會把requestAnimationFrame方法歸類為和setTimeout一樣都屬于宏任務(wù)隊列挽荡,但是實際上來說與宏任務(wù)和微任務(wù)無關(guān)它只和瀏覽器渲染相關(guān)藐石,在瀏覽器刷新前執(zhí)行