從瀏覽器渲染到性能優化

      網友投稿 876 2025-04-01

      從瀏覽器渲染到性能優化

      問題前瞻

      1.?為什么css需要放在頭部? 2.?js為什么要放在body后面? 3.?圖片的加載和渲染會阻塞頁面DOM構建嗎? 4.?dom解析完才出現頁面嗎? 5.?首屏時間根據什么來判定?

      瀏覽器渲染

      1.瀏覽器渲染圖解

      [來自google開發者文檔]

      瀏覽器渲染頁面主要經歷了下面的步驟:

      1.處理?HTML?標記并構建?DOM?樹。 2.處理?CSS?標記并構建?CSSOM?樹。 3.將?DOM?與?CSSOM?合并成一個渲染樹。 4.根據渲染樹來布局,以計算每個節點的幾何信息。 5.將各個節點繪制到屏幕上。

      為構建渲染樹,瀏覽器大體上完成了下列工作:

      從?DOM?樹的根節點開始遍歷每個可見節點。 某些節點不可見(例如腳本標記、元標記等),因為它們不會體現在渲染輸出中,所以會被忽略。 某些節點通過?CSS?隱藏,因此在渲染樹中也會被忽略,例如,上例中的?span?節點---不會出現在渲染樹中,---因為有一個顯式規則在該節點上設置了“display:?none”屬性。 對于每個可見節點,為其找到適配的?CSSOM?規則并應用它們。 發射可見節點,連同其內容和計算的樣式。

      根據以上解析,DOM樹和CSSOM樹的構建對于頁面性能有非常大的影響,沒有DOM樹,頁面基本的標簽塊都沒有,沒有樣式,頁面也基本是空白的。所以具體css的解析規則是什么?js是怎么影響頁面渲染的?了解了這些,我們才能有的放矢,對頁面性能進行優化。

      2.css解析規則

      ???? ???????? ???????????? ????????????...???? ????????

      ???? ???????????? ???????????????? ????????????????...???? ????????????
      ???? ???????????????? ????????????????...???? ????????????
      ???? ????????
      ???? ????
      ???? ???????? ???????????? ???????????????? ????????????????...???? ????????????
      ???? ???????????? ????????

      #div1?.c?.d?{}???? .f?.c?.d?{}???? .a?.c?.e?{}???? #div1?.f?{}???? .c?.d{}

      層級減少,意味者匹配時遍歷的dom就少。

      如果css從左向右解析,意味著我們需要遍歷更多的節點。不管樣式規則寫得多細致,每一個dom結點仍然需要遍歷,因為整個style rules還會有其它公共樣式影響。如果從右向左解析,因為子元素只有一個父元素,所以能夠很快定位出當前dom符不符合樣式規則。

      3.js加載和執行機制

      首先明確一點,我們可以通過js去修改網頁的內容,樣式和交互等,這一意味著js會影響頁面的dom結構,如果js和dom構建并行執行,那么很容易會出現沖突,所以js在執行時必然會阻塞dom和cssom的構建過程,不論是外部js還是內聯腳本。

      首先我們為什么提倡把js放在body標簽的后面去加載,因為從demo上看無論是放在head還是放在body后加載js,頁面domcontentload的時間都是一樣的:

      我們從圖中可以看出js的加載和執行是阻塞dom解析的,但是因為頁面并不是一次就渲染完成,所以我們需要做的是盡量讓用戶看到首屏的部分被渲染出來,js放在頭部,則頁面的內容區域還沒有解析到就被阻塞了,導致用戶看到的是白屏,而js放在body后面,盡管此時頁面dom仍然沒有解析完成,但是已經渲染出一部分樓層了,這也是為什么我們比較看重頁面的首屏時間。

      只有DOM和CSSOM樹構建好后并合并成渲染樹才能開始繪制頁面圖形,那是不是把整個DOM樹和CSSOM樹構建好后才能開始繪制頁面?這顯然是不符合我們平時訪問頁面的認知的,實際上:

      為達到更好的用戶體驗,呈現引擎會力求盡快將內容顯示在屏幕上。它不必等到整個 HTML 文檔解析完畢之后,就會開始構建呈現樹和設置布局。在不斷接收和處理來自網絡的其余內容的同時,呈現引擎會將部分內容解析并顯示出來。

      具體瀏覽器什么時候進行首次繪制?可以查看本文對瀏覽器首次渲染時間點的探究。

      4.圖片的加載和渲染機制

      首先我們解答一下上面的問題:圖片的加載與渲染會不會阻塞頁面渲染?答案是圖片的加載和渲染不會影響頁面的渲染。

      那么標簽中的圖片和樣式中的圖片的加載和渲染時間是什么樣的呢?

      解析HTML【遇到標簽加載圖片】?—>?構建DOM樹 加載樣式?—>?解析樣式【遇到背景圖片鏈接不加載】?—>?構建樣式規則樹 加載javascript?—>?執行javascript代碼 把DOM樹和樣式規則樹匹配構建渲染樹【遍歷DOM樹時加載對應樣式規則上的背景圖片】 計算元素位置進行布局 繪制【開始渲染圖片】

      當然把DOM樹和樣式規則樹匹配構建渲染樹時,只會把可見元素和它對應的樣式規則結合一起產出到渲染樹,這就意味有不可見元素,當匹配DOM樹和樣式規則樹時,若發現一個元素的對應的樣式規則上有display:none,瀏覽器會認為該元素是不可見的,因此不會把該元素產出到渲染樹上。

      性能優化

      css優化

      #div?p.class?{???? ????color:?red;???? }???? ???? .class?{???? ????color:?red;???? }

      從瀏覽器渲染到性能優化

      層級減少,意味者匹配時遍歷的dom就少。

      減少匹配次數

      (function(){???? ????window.gConfig?=??window.gConfig?||?{};???? ????window.gConfig.isMobile?=?/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);???? ????var?hClassName;???? ????if(window.gConfig.isMobile){???? ????????hClassName?=?'?phone';???? ???? ????????document.write('

      [來自https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html]

      如果腳本是模塊化的并且不依賴于任何腳本,請使用async。

      如果該腳本依賴于另一個腳本或由另一個腳本所依賴,則使用defer。

      減少資源請求

      瀏覽器的并發數量有限,所以為了減少瀏覽器因為優先加載很多不必要資源,以及網絡請求和響應時間帶來的頁面渲染阻塞時間,我們首先應該想到的是減少頁面加載的資源,能夠盡量用壓縮合并,懶加載等方法減少頁面的資源請求。

      延遲加載圖像

      盡管圖片的加載和渲染不會影響頁面渲染,但是為了盡可能地優先展示首屏圖片和減少資源請求數量,我們需要對圖片做懶加載。

      document.addEventListener("DOMContentLoaded",?function()?{???? ????let?lazyImages?=?[].slice.call(document.querySelectorAll("img.lazy"));???? ????let?active?=?false;???? ???? ????const?lazyLoad?=?function()?{???? ????????if?(active?===?false)?{???? ????????????active?=?true;???? ???? ????????????setTimeout(function()?{???? ????????????????lazyImages.forEach(function(lazyImage)?{???? ????????????????????if?((lazyImage.getBoundingClientRect().top?<=?window.innerHeight?&&?lazyImage.getBoundingClientRect().bottom?>=?0)?&&?getComputedStyle(lazyImage).display?!==?"none")?{???? ????????????????????lazyImage.src?=?lazyImage.dataset.src;???? ????????????????????lazyImage.srcset?=?lazyImage.dataset.srcset;???? ????????????????????lazyImage.classList.remove("lazy");???? ???? ????????????????????lazyImages?=?lazyImages.filter(function(image)?{???? ????????????????????return?image?!==?lazyImage;???? ????????????????????});???? ???? ????????????????????if?(lazyImages.length?===?0)?{???? ????????????????????document.removeEventListener("scroll",?lazyLoad);???? ????????????????????window.removeEventListener("resize",?lazyLoad);???? ????????????????????window.removeEventListener("orientationchange",?lazyLoad);???? ????????????????????}???? ????????????????}???? ????????????????});???? ???? ????????????????active?=?false;???? ????????????},?200);???? ????????}???? ????};???? ???? ????document.addEventListener("scroll",?lazyLoad);???? ????window.addEventListener("resize",?lazyLoad);???? ????window.addEventListener("orientationchange",?lazyLoad);???? });

      詳情參考延遲加載圖像和視頻

      大促活動實踐

      2.1 懶加載與異步加載

      懶加載與異步加載是大促活動性能優化的主要手段,直白的說就是把用戶不需要或者不會立即看到的頁面數據與內容全都挪到頁面首屏渲染完成之后去加載,極限減小頁面首屏渲染的數據加載量與js,css執行帶來的性能損耗。

      導航的下拉內容是一塊結構非常復雜的html片段,如果直接加載,瀏覽器渲染的時間會拖慢頁面整體的加載時間:

      所有我們需要通過異步加載方式來獲取這段html片段,等頁面首屏渲染結束后再添加到頁面上,大致的代碼如下:

      $.ajax({???? ????url:?url,?async:?false,?timeout:?10000,???? ????success:?function?(data)?{???? ????????container.innerHTML?=?data;???? ????????var?appendHtml?=?$(''?+?container.querySelector('#footer').innerHTML?+?'');???? ????????var?tempHtml?=?''?+?''?+?appendHtml.find('#header-lazyload-html-drop').html()?+?'<\/script>'?+?appendHtml.find('#header-lazyload-html-mbnav').html()?+?'<\/script>';???? ????????$('#footer').append(tempHtml);???? ????????????feloader.onLoad(function?()?{???? ????????????feloader.use('@cloud/common-resource/header',?function?()?{???? ????????????});???? ????????????$('#footer').css('display',?'block');???? ????????});???? ????},???? ????error:?function?(XMLHttpRequest,?textStatus,?errorThrown)?{???? ????????console.log(XMLHttpRequest.status,?XMLHttpRequest.readyState,?textStatus);???? ????},???? });

      官網的開發腳手架中已經有lazyload的插件支持圖片懶加載,使用方法頁非常簡單:

      ???? ????

      同時官網的頁腳部分也采用了采用其它的加載方式也實現了懶加載的效果,頁腳的圖片都在css中引用,想要延遲加載頁腳圖片就需要延遲加載頁腳的css,但是延遲加載css造成的后果就是頁面加載的一瞬間頁腳會因為樣式確實而顯示錯亂,所以我們可以在css樣式加載前強勢隱藏掉頁腳部分,等css加載完成后,頁腳dom自帶的display:block會自動顯示頁腳。(==因為頁腳的seo特性沒有對其進行懶加載==)

      基于xtpl自帶的懶加載能力,配合pep定制頁面模板的邏輯,我們可以實現html的懶加載。在頁面初次渲染的時候,只有每個樓層的大體框架和標題等關鍵信息,如果需要的話可以給默認圖片等占位,或設置最小高度占位,防止錨點定位失效。

      當頁面滾動到該樓層的位置,js代碼方會執行,在初始化函數中,對該樓層的html進行加載,渲染,實現樓層圖片和html的懶加載,減少了首屏時間。

      具體代碼如下:

      ????

      public?render(){???? ????this.$el.find('.j-content').html(new?Xtemplate(tpl).render(mockData))???? ????...???? }

      套餐數據的加載一直以來都是令人頭疼的,本次雙十一對于套餐腳本也做了優化,不僅對數據進行了緩存,同時也可以在指定的范圍進行套餐數據的渲染——和上述所說的樓層懶加載配合,可以做到未展示的樓層,套餐數據不請求,下拉框不渲染,詢價接口不調用,在首屏不出現大量套餐的情況下,可以大大提升首屏加載的性能。

      2.2.資源整合

      基礎模板的優化涉及到資源的合并,壓縮與異步加載,dom的延遲加載和圖片的懶加載。首先我們給出官網基礎模板引用的一部分js資源的表格:

      這部分js存在問題是分散在pep的各個資產庫路徑維護,有些壓縮了,有些沒有壓縮,js的加載也基本是順序執行,所以我們對這個部分的js和css資源進行了一個整合,進行的操作是遷移,合并,壓縮。

      建立common-resource倉庫去統一維護管理頁頭頁腳及公共資源代碼。

      common.js

      import?'./common/js/AGrid';???? import?'./common/js/jquery.base64';???? import?'./common/js/lang-tips';???? import?'./common/js/setLocaleCookie';???? import?'./common/js/pepDialog';

      如上面代碼,將官網中用的分散的基礎功能js合并成一個common.js,經過伏羲流水線發布,cui套件會自動將js壓縮,這樣做的效果當然是減少官網頁面請求資源數,減小資源大小。

      觀察2.2.1中的表格可以發現,官網大部分js都是放在頭部或者是body后順序加載的,這些資源的加載時間必定是在DOMOnLoad之前

      這些js都是會阻塞頁面的渲染,導致頁面首屏加載變慢,我們需要做的就是通過之前頭尾資源的整理得出哪些資源是可以在onload之后去加載的,這些我們就可以把頁面加載時不需要執行的js和css全部移到頁面渲染完成后去加載,少了這部分的js邏輯執行時的阻塞,頁面首屏渲染的時間也會大大降低。

      通過cui套件中的feloader插件,我們可以比較便捷的控制js和css加載的時機:

      feloader.onLoad(function?()?{???? ??feloader.use([???? ????'@cloud/link-to/index',???? ????'@cloud/common-resource/uba',???? ????'@cloud/common-resource/footer',???? ????'@cloud/common-resource/header',???? ????'@cloud/common-resource/common',???? ????'@cloud/common-resource/prompt.css',???? ????'@cloud/common-resource/footer.css',???? ??]);???? });

      下圖可以明顯看到js的加載都轉移到onload之后了:

      除了對設計給出的圖片有壓縮要求外,我們還通過對一部分不常更新的小圖標圖片進行base64編碼來減少頁面的圖片請求數量。

      2.3預解析與預加載

      除了延遲加載外,基礎模板還進行了諸如dns預解析,資源預加載的手段來提前解析dns和加載頁面資源。

      當用戶訪問過官網頁面后,DNS預解析能夠使用戶在訪問雙十一活動頁之前提前進行DNS解析,從而減少雙十一活動頁面的dns解析時間,提高頁面的訪問性能,其實寫法也很簡單:

      活動頁的部分js還使用了preload預加載的方式來提升頁面加載性能,preload的為什么可以達到這種效果,我們需要看下面這段摘錄:

      Preloader 簡介

      HTML 解析器在創建 DOM 時如果碰上同步腳本(synchronous script),解析器會停止創建 DOM,轉而去執行腳本。所以,如果資源的獲取只發生在解析器創建 DOM時,同步腳本的介入將使網絡處于空置狀態,尤其是對外部腳本資源來說,當然,頁面內的腳本有時也會導致延遲。

      預加載器(Preloader)的出現就是為了優化這個過程,預加載器通過分析瀏覽器對 HTML 文檔的早期解析結果(這一階段叫做“令牌化(tokenization)”),找到可能包含資源的標簽(tag),并將這些資源的 URL 收集起來。令牌化階段的輸出將會送到真正的 HTML 解析器手中,而收集起來的資源 URLs 會和資源類型一起被送到讀取器(fetcher)手中,讀取器會根據這些資源對頁面加載速度的影響進行有次序地加載。

      基于以上原理,我們對官網相對重要的js資源進行preload預加載,以使得瀏覽器可以盡快地加載頁面所需的重要資源。

      優化效果

      3.總結

      前端性能優化的方法手段并不僅限于文章陳述,官網前端團隊還會在前端性能優化的道路上學習更多,探索更多,將華為云官網頁面的加載性能做到極致!

      渲染 CSS

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      標簽:什么時候 性能優化 setTimeout
      上一篇:WPS表格設置邊框顏色的方法步驟(wps中如何設置表格邊框顏色)
      下一篇:在線表格有哪些優勢?
      相關文章
      伊人久久大香线蕉亚洲五月天| 亚洲Av永久无码精品一区二区| 亚洲国产精品狼友中文久久久| 亚洲乱人伦中文字幕无码| 亚洲一区精彩视频| 精品亚洲AV无码一区二区| 亚洲女人初试黑人巨高清| 亚洲福利电影一区二区?| 亚洲精品乱码久久久久久下载| 亚洲综合激情另类小说区| 久久av无码专区亚洲av桃花岛| 亚洲综合一区二区精品导航| 亚洲日本在线观看| 亚洲网站在线播放| 亚洲一级毛片免费看| 国产精品亚洲综合久久| 亚洲一区二区三区丝袜| 亚洲视频无码高清在线| 亚洲精品无码中文久久字幕| 国产精品亚洲综合天堂夜夜| 亚洲国产精品成人久久蜜臀| 国产精品亚洲精品日韩已方| 在线亚洲精品福利网址导航| 久久久久久久尹人综合网亚洲| 久久精品国产亚洲av成人| 久久亚洲精品成人av无码网站 | 亚洲精品亚洲人成在线麻豆| 亚洲三级在线视频| 亚洲丶国产丶欧美一区二区三区 | 亚洲av日韩av不卡在线观看| 亚洲视频欧洲视频| 久久亚洲最大成人网4438| 亚洲人成色777777精品| 在线观看亚洲精品专区| 久久久久亚洲精品男人的天堂| 亚洲爆乳无码一区二区三区| 在线观看亚洲人成网站| 国产亚洲精aa在线看| 亚洲国产精品毛片av不卡在线| 亚洲热线99精品视频| 久久亚洲精品无码AV红樱桃|