通過(guò)瀏覽器渲染過(guò)程來(lái)進(jìn)行前端優(yōu)化
介紹
做web開(kāi)發(fā),我個(gè)人覺(jué)得必須要弄清楚瀏覽器的渲染過(guò)程,否則我們很難進(jìn)行前端優(yōu)化。
我今天就簡(jiǎn)單說(shuō)一下頁(yè)面加載和前端優(yōu)化。
頁(yè)面加載
我按照最簡(jiǎn)單的方式進(jìn)行描述,實(shí)際上更復(fù)雜,不管是在瀏覽器端還是服務(wù)端,比如dns解析,代理服務(wù)器,負(fù)載均衡器等等。
1、用戶訪問(wèn)網(wǎng)頁(yè),發(fā)送一個(gè)http請(qǐng)求到網(wǎng)絡(luò)服務(wù)器。
2、網(wǎng)絡(luò)服務(wù)器(應(yīng)用服務(wù)器)解析請(qǐng)求,發(fā)送請(qǐng)求給數(shù)據(jù)庫(kù)服務(wù)器。
3、數(shù)據(jù)服務(wù)器返回?cái)?shù)據(jù)給網(wǎng)絡(luò)服務(wù)器,網(wǎng)絡(luò)服務(wù)器解析數(shù)據(jù),并生成html文件內(nèi)容放入http response中,返回給瀏覽器。
4、瀏覽器解析http response。
5、瀏覽器創(chuàng)建DOM樹(shù)。
6、瀏覽器下載css,并應(yīng)用在DOM樹(shù)上,進(jìn)行渲染。
7、瀏覽器下載js,并解析執(zhí)行js。
缺陷
以上整個(gè)流程中,如果其中任何一個(gè)流程出現(xiàn)問(wèn)題,都不能順利的渲染頁(yè)面。
服務(wù)端:
網(wǎng)絡(luò)服務(wù)器:無(wú)法獲取到資源文件(404),或者由于并發(fā)的原因暫時(shí)無(wú)法處理你的請(qǐng)求(最常見(jiàn)的500錯(cuò)誤),你的瀏覽器會(huì)長(zhǎng)時(shí)間處于空白狀態(tài),直到服務(wù)器返回狀態(tài),或者進(jìn)行超時(shí)處理。
數(shù)據(jù)層:如果服務(wù)器停止,或忙于處理大數(shù)據(jù)等等,長(zhǎng)時(shí)間無(wú)法返回?cái)?shù)據(jù)給網(wǎng)絡(luò)服務(wù)器,那么網(wǎng)絡(luò)服務(wù)器一直處于等待狀態(tài)中,如果請(qǐng)求量達(dá)到最大值,那么后面的請(qǐng)求都被堵塞,從而無(wú)法及時(shí)返回內(nèi)容給瀏覽器。
客戶端:
JavaScript:如果你的js寫(xiě)在body中的div里,而且這個(gè)js執(zhí)行非常復(fù)雜的邏輯,那么整個(gè)頁(yè)面處于等待狀態(tài)中。
不論js代碼是內(nèi)聯(lián)還是包含在一個(gè)不相干的外部文件中,頁(yè)面下載和解析過(guò)程肯定會(huì)停下,等待腳本執(zhí)行完成這些處理,然后才能繼續(xù)進(jìn)行。——大多數(shù)瀏覽器使用單進(jìn)程處理JavaScript的多個(gè)任務(wù),同一時(shí)間只能有一個(gè)任務(wù)執(zhí)行。
CSS:可以同時(shí)下載多個(gè)CSS文件。
如果我們把CSS樣式放在頁(yè)面底部,雖然使頁(yè)面內(nèi)容能更快的加載(因?yàn)閷⒓虞dcss 文件的時(shí)間放在最后,從而使頁(yè)面內(nèi)容先顯示出來(lái)),但這樣的內(nèi)容是沒(méi)有樣式的,在CSS文件加載進(jìn)來(lái)后,瀏覽器再對(duì)DOM使用樣式,會(huì)出現(xiàn)我們常說(shuō)的“無(wú)樣式之閃爍”。
更討厭的是,上下都放置CSS樣式,瀏覽器會(huì)首先按照上面的進(jìn)行渲染,等到下面的樣式上來(lái),再按照下面的樣式進(jìn)行回流和重繪,用戶感覺(jué)很差。
注意兩個(gè)詞“repaint"和"reflow"。
repaint(重繪)是在一個(gè)元素的外觀被改變,但沒(méi)有改變布局的情況下發(fā)生。——如果只是改變某個(gè)元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內(nèi)部布局的屬性,將只會(huì)引起瀏覽器repaint。
reflow(回流):瀏覽器發(fā)現(xiàn)某個(gè)部分發(fā)生了點(diǎn)變化影響了布局,需要倒回去重新渲染,這個(gè)回退的過(guò)程就叫回流。
總結(jié):以上兩種嚴(yán)重影響用戶體驗(yàn),會(huì)無(wú)意識(shí)的流失用戶。
#p#
解決方案
服務(wù)端:方式比較多,可以從架構(gòu)上說(shuō)(這個(gè)內(nèi)容太多了,什么負(fù)載均衡了,什么緩存了,什么主從了),但是今天主要討論語(yǔ)言層面。
我們可以使用逐步返回內(nèi)容的方式,輸送數(shù)據(jù)給瀏覽器,如我們可以使用php的flush,把整個(gè)head部分,半個(gè)body加一部分div返回給瀏覽器,進(jìn)行渲染,然后把其他部分逐步輸送到瀏覽器。
我們可以在服務(wù)端使用多線程或多進(jìn)程的方式并發(fā)去進(jìn)行數(shù)據(jù)處理。如php常見(jiàn)的
- do {
- $mrc = curl_multi_exec($mh, $active);
- }while($mrc==CURLM_CALL_MULTI_PERFORM);
- while ($active && $mrc == CURLM_OK){
- if (curl_multi_select($mh) != -1){
- do {
- $mrc = curl_multi_exec($mh,$active);
- }while($mrc==CURLM_CALL_MULTI_PERFORM);
- }
- }
或者
- <?php
- while (count($sockets)) {
- $read = $write = $sockets;
- $n = stream_select($read,$write, $e, $timeout);
- if ($n > 0) {
- foreach ($read as $r) {
- $id = array_search($r, $sockets);
- $data = fread($r, 8192);
- if (strlen($data) == 0) {
- fclose($r);
- unset ($sockets[$id]);
- }else {
- $retdata[$id] .= $data;
- }
- }
- $retdata[$id] = preg_replace('/^HTTP(.*?)\r\n\r\n/is',<em>, $retdata[$id]);</em>
- foreach ($write as $w) {
- if (!is_resource($w))continue;
- $id = array_search($w, $sockets);
- fwrite($w, "GET /" . $url[$id] . "HTTP/1.0\r\nHost: " . $hosts[$id] ."\r\n\r\n");
- $status[$id] = 1;
- }
- }else {
- break;
- }
- }
JavaScript:
1、把腳本進(jìn)行壓縮(移除不必要的字符,注釋以及空行)。
2、對(duì)部分js文件進(jìn)行合并,以減少http的請(qǐng)求個(gè)數(shù),以減少服務(wù)器端的壓力——但是要量力而行,因?yàn)槿绻愕膉s文件很大,下載很慢的話,很多功能都不能正常進(jìn)行,我們可以按照業(yè)務(wù)進(jìn)行合并。
3、使用外部js文件。因?yàn)楝F(xiàn)在很多瀏覽器都有緩存,明顯會(huì)減少http請(qǐng)求數(shù)。
4、將腳本放在頁(yè)面底部。先讓用戶看到內(nèi)容,然后再加載js,這樣用戶會(huì)感覺(jué)頁(yè)面加載速度很快。
CSS:
1、合并多個(gè)css文件,以減少http的請(qǐng)求個(gè)數(shù),以減少服務(wù)器端的壓力。
2、使用外部css文件。主要原因是瀏覽器緩存,以減少http請(qǐng)求。
3、放在頁(yè)面頂部(head標(biāo)簽處),防止出現(xiàn)“無(wú)樣式內(nèi)容的閃爍”。
原文鏈接:http://www.cnblogs.com/baochuan/archive/2012/05/29/2524694.html
【編輯推薦】