用戶(hù)第一次訪(fǎng)問(wèn)網(wǎng)址,瀏覽器向服務(wù)器發(fā)出請(qǐng)求,服務(wù)器返回html文件;
瀏覽器開(kāi)始載入html代碼,發(fā)現(xiàn) head 標(biāo)簽內(nèi)有一個(gè) link 標(biāo)簽引用外部CSS或JS文件;
瀏覽器又發(fā)出CSS及JS文件的請(qǐng)求,服務(wù)器返回這個(gè)CSS,JS文件;
瀏覽器繼續(xù)載入html中 body 部分的代碼,并且CSS,JS文件已經(jīng)拿到手了,可以開(kāi)始渲染頁(yè)面了;
瀏覽器在代碼中發(fā)現(xiàn)一個(gè) img 標(biāo)簽引用了一張圖片,向服務(wù)器發(fā)出請(qǐng)求。此時(shí)瀏覽器不會(huì)等到圖片下載完,而是繼續(xù)渲染后面的代碼;
服務(wù)器返回圖片文件,由于圖片占用了一定面積,影響了頁(yè)面布局,因此瀏覽器需要回過(guò)頭來(lái)重新渲染這部分代碼;
瀏覽器發(fā)現(xiàn)了一個(gè)包含一行Javascript代碼的 script 標(biāo)簽,趕快執(zhí)行它;
Javascript腳本執(zhí)行了這條語(yǔ)句,它命令瀏覽器隱藏掉代碼中的某個(gè) p (style.display=”none”)。杯具啊,突然就少了這么一個(gè)元素,瀏覽器不得不重新渲染這部分代碼;
終于等到了 html 的到來(lái),瀏覽器淚流滿(mǎn)面……
IE瀏覽器下載的順序是從上到下,渲染的順序也是從上到下,下載和渲染是同時(shí)進(jìn)行的。
在渲染到頁(yè)面的某一部分時(shí),其上面的所有部分都已經(jīng)下載完成(并不是說(shuō)所有相關(guān)聯(lián)的元素都已經(jīng)下載完)
如果遇到語(yǔ)義解釋性的標(biāo)簽嵌入文件(JS腳本,CSS樣式),那么此時(shí)IE的下載過(guò)程會(huì)啟用單獨(dú)連接進(jìn)行下載。
并且在下載后進(jìn)行解析,解析過(guò)程中,停止頁(yè)面所有往下元素的下載,阻塞加載。
樣式表在下載完成后,將和以前下載的所有樣式表一起進(jìn)行解析,解析完成后,將對(duì)此前所有元素(含以前已經(jīng)渲染的)重新進(jìn)行渲染。
JS、CSS中如有重定義,后定義函數(shù)將覆蓋前定義函數(shù)。
不能并行下載和解析(阻塞下載)
web的模式是同步的,開(kāi)發(fā)者希望解析到一個(gè)script標(biāo)簽時(shí)立即解析執(zhí)行腳本,并阻塞文檔的解析直到腳本執(zhí)行完;如果腳本是外引的,當(dāng)引用了JS的時(shí)候,瀏覽器發(fā)送一個(gè)js request就會(huì)一直等待該request的返回,這個(gè)過(guò)程也是同步的,會(huì)阻塞文檔的解析直到資源被請(qǐng)求到。因?yàn)闉g覽器需要一個(gè)穩(wěn)定的DOM樹(shù)結(jié)構(gòu),而JS中很有可能有代碼直接改變了DOM樹(shù)結(jié)構(gòu),比如使用 document.write 或 appendChild,甚至是直接使用的location.href進(jìn)行跳轉(zhuǎn),瀏覽器為了防止出現(xiàn)JS修改DOM樹(shù),需要重新構(gòu)建DOM樹(shù)的情況,所以就會(huì)阻塞其他的下載和呈現(xiàn)。這個(gè)模式保持了很多年,并且在html4及html5中都特別指定了。開(kāi)發(fā)者可以將腳本標(biāo)識(shí)為defer,以使其不阻塞文檔解析,并在文檔解析結(jié)束后執(zhí)行。Html5增加了標(biāo)記腳本為異步的選項(xiàng),以使腳本的解析執(zhí)行使用另一個(gè)線(xiàn)程。
這里面有幾個(gè)點(diǎn)需要說(shuō)明一下:
1.我們知道瀏覽器的處理過(guò)程是解析html生成DOM tree->根據(jù)DOM tree和樣式表生成render tree->渲染render tree展示。瀏覽器為了讓用戶(hù)更快的看到頁(yè)面,所以是邊解析html生成局部的DOM tree,瀏覽器就生成部分render tree然后展示出來(lái)。
2.此過(guò)程中有兩種外部資源是阻塞腳本執(zhí)行,從而阻塞渲染的,分別是外部js和外部css。外部js是阻塞了DOM tree的生成,因?yàn)闉g覽器需要一個(gè)穩(wěn)固的DOM tree,而js可能破壞這個(gè)結(jié)構(gòu)(當(dāng)然其中也可能會(huì)更改樣式【注意是樣式而不是樣式表】,但是這個(gè)不阻塞也不會(huì)有影響的);外部css樣式表也會(huì)阻塞腳本的執(zhí)行,理論上,既然樣式表不改變Dom樹(shù),也就沒(méi)有必要停下文檔的解析等待它們,然而,存在一個(gè)問(wèn)題,腳本可能在文檔的解析過(guò)程中請(qǐng)求樣式信息,如果樣式還沒(méi)有加載和解析,腳本將得到錯(cuò)誤的值,顯然這將會(huì)導(dǎo)致很多問(wèn)題,這看起來(lái)是個(gè)邊緣情況,但確實(shí)很常見(jiàn)。Firefox在存在樣式表還在加載和解析時(shí)阻塞所有的腳本,而Chrome只在當(dāng)腳本試圖訪(fǎng)問(wèn)某些可能被未加載的樣式表所影響的特定的樣式屬性時(shí)才阻塞這些腳本。
3.其他外部資源是不阻塞渲染的,比如圖片,我們能看到很多時(shí)候頁(yè)面大體的框架都呈現(xiàn)出來(lái)了,就是圖片的位置沒(méi)有顯示出來(lái)的情況,等到圖片下載下來(lái)以后再重新渲染。
現(xiàn)代瀏覽器的優(yōu)化:
按照標(biāo)準(zhǔn)的瀏覽器渲染和下載過(guò)程。下面的代碼加載外部資源的順序應(yīng)該和資源在html中的順序一致。其中head中添加了一個(gè)外部資源請(qǐng)求http://#/hm.js?a041a0f4ff93aef6aa83f34134331a1d應(yīng)該在所有樣式之前加載
<html> <head>...百度統(tǒng)計(jì)代碼--> <script>var _hmt = _hmt || []; (function() {var host=document.location.hostname;if(/lcfarm.com$/ig.test(host)){ var hm = document.createElement("script"); hm.src = "//#/hm.js?a041a0f4ff93aef6aa83f34134331a1d"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); } })();script> <link rel="stylesheet" type="text/css" href="//static.lcfarm.com/pc-dist/pkg/index.html_aio_f9db6a6.css?1.1.2"> <link rel="stylesheet" type="text/css" href="//static.lcfarm.com/pc-dist/common/css/common_530eedd.css?1.1.2"> <link rel="stylesheet" type="text/css" href="//static.lcfarm.com/pc-dist/css/index_8b620da.css?1.1.2"> <link rel="stylesheet" type="text/css" href="//static.lcfarm.com/pc-dist/pkg/index.html_aio_2_2379650.css?1.1.2">head> <body>... <script type="text/javascript" data-loader="" src="//static.lcfarm.com/pc-dist/common/dep/mod_36ad799.js?1.1.2">script> <script type="text/javascript" data-loader="" src="//static.lcfarm.com/pc-dist/common/dep/jquery_c07f226.js?1.1.2">script> <script type="text/javascript" src="//static.lcfarm.com/pc-dist/common/js/jquery/jquery.cookie_546183c.js?1.1.2">script> <script type="text/javascript" src="//static.lcfarm.com/pc-dist/pkg/common_85ea494.js?1.1.2">script> <script type="text/javascript" src="//static.lcfarm.com/pc-dist/pkg/index.html_aio_350303c.js?1.1.2">script>body>html>
但是實(shí)際上在chrome。Firefox、ie8+等瀏覽器中卻發(fā)現(xiàn)是如下效果(使用https://www.webpagetest.org/測(cè)試)
為什么?這就是預(yù)解析(Speculative parsing)
Webkit和Firefox都做了這個(gè)優(yōu)化,當(dāng)執(zhí)行腳本時(shí),另一個(gè)線(xiàn)程解析剩下的文檔,并加載后面需要通過(guò)網(wǎng)絡(luò)加載的資源。這種方式可以使資源并行加載從而使整體速度更快。需要注意的是,預(yù)解析并不改變Dom樹(shù),它將這個(gè)工作留給主解析過(guò)程,自己只解析外部資源的引用,比如外部腳本、樣式表及圖片。
如上面的那張圖,可以看出在執(zhí)行腳本的時(shí)候與解析了一大堆的外部資源引用,并啟動(dòng)線(xiàn)程下載他們,主線(xiàn)程還在等待hm.js的返回。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com