整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          CSS加載會阻塞頁面顯示?

          能大家都知道,js執行會阻塞DOM樹的解析和渲染,那么css加載會阻塞DOM樹的解析和渲染嗎?接下來,我們就一起來分析一下。

          原理解析

          那么為什么會出現上面的現象呢?我們從瀏覽器的渲染過程來解析下。

          不同的瀏覽器使用的內核不同,所以他們的渲染過程也是不一樣的。目前主要有兩個:

          webkit渲染過程

          Gecko渲染過程

          從上面兩個流程圖我們可以看出來,瀏覽器渲染的流程如下:

          1. HTML解析文件,生成DOM Tree,解析CSS文件生成CSSOM Tree
          2. 將Dom Tree和CSSOM Tree結合,生成Render Tree(渲染樹)
          3. 根據Render Tree渲染繪制,將像素渲染到屏幕上。

          從流程我們可以看出來

          1. DOM解析和CSS解析是兩個并行的進程,所以這也解釋了為什么CSS加載不會阻塞DOM的解析。
          2. 然而,由于Render Tree是依賴于DOM Tree和CSSOM Tree的,所以他必須等待到CSSOM Tree構建完成,也就是CSS資源加載完成(或者CSS資源加載失敗)后,才能開始渲染。因此,CSS加載是會阻塞Dom的渲染的。
          3. 由于js可能會操作之前的Dom節點和css樣式,因此瀏覽器會維持html中css和js的順序。因此,樣式表會在后面的js執行前先加載執行完畢。所以css會阻塞后面js的執行。

          DOMContentLoaded

          對于瀏覽器來說,頁面加載主要有兩個事件,一個是DOMContentLoaded,另一個是onLoad。而onLoad沒什么好說的,就是等待頁面的所有資源都加載完成才會觸發,這些資源包括css、js、圖片視頻等。

          而DOMContentLoaded,顧名思義,就是當頁面的內容解析完成后,則觸發該事件。那么,正如我們上面討論過的,css會阻塞Dom渲染和js執行,而js會阻塞Dom解析。那么我們可以做出這樣的假設

          1. 當頁面只存在css,或者js都在css前面,那么DomContentLoaded不需要等到css加載完畢。
          2. 當頁面里同時存在css和js,并且js在css后面的時候,DomContentLoaded必須等到css和js都加載完畢才觸發。

          我們先對第一種情況做測試:

          <!DOCTYPE html>
          <html lang="en">
           <head>
           <title>css阻塞</title>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1">
           <script>
           document.addEventListener('DOMContentLoaded', function() {
           console.log('DOMContentLoaded');
           })
           </script>
           <link  rel="stylesheet">
           </head>
           <body>
           </body>
          </html>
          

          實驗結果如下圖:

          從動圖我們可以看出來,css還未加載完,就已經觸發了DOMContentLoaded事件了。因為css后面沒有任何js代碼。

          接下來我們對第二種情況做測試,很簡單,就在css后面加一行代碼就行了

          <!DOCTYPE html>
          <html lang="en">
           <head>
           <title>css阻塞</title>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1">
           <script>
           document.addEventListener('DOMContentLoaded', function() {
           console.log('DOMContentLoaded');
           })
           </script>
           <link  rel="stylesheet">
           <script>
           console.log('到我了沒');
           </script>
           </head>
           <body>
           </body>
          </html>
          

          實驗結果如下圖:

          我們可以看到,只有在css加載完成后,才會觸發DOMContentLoaded事件。因此,我們可以得出結論:

          1. 如果頁面中同時存在css和js,并且存在js在css后面,則DOMContentLoaded事件會在css加載完后才執行。
          2. 其他情況下,DOMContentLoaded都不會等待css加載,并且DOMContentLoaded事件也不會等待圖片、視頻等其他資源加載。

          總結

          由上所述,我們可以得出以下結論:

          1. css加載不會阻塞DOM樹的解析
          2. css加載會阻塞DOM樹的渲染
          3. css加載會阻塞后面js語句的執行、

          因此,為了避免讓用戶看到長時間的白屏時間,我們應該盡可能的提高css加載速度,比如可以使用以下幾種方法:

          1. 使用CDN(因為CDN會根據你的網絡狀況,替你挑選最近的一個具有緩存內容的節點為你提供資源,因此可以減少加載時間)
          2. 對css進行壓縮(可以用很多打包工具,比如webpack,gulp等,也可以通過開啟gzip壓縮)
          3. 合理的使用緩存(設置cache-control,expires,以及E-tag都是不錯的,不過要注意一個問題,就是文件更新后,你要避免緩存而帶來的影響。其中一個解決防范是在文件名字后面加一個版本號)
          4. 減少http請求數,將多個css文件合并,或者是干脆直接寫成內聯樣式(內聯樣式的一個缺點就是不能緩存)

          文由 ChatMoney團隊出品

          在我們開發網站應用時,我們可能會遇到腳本加載失敗的情況,導致腳本加載失敗的原因有很多,比如用戶的網絡問題、終端設備問題、用戶瀏覽器版本等諸多因素。

          解決方案

          在 JavaScript 中,我們可以創建一個監聽來監聽腳本加載失敗的情況,然后針對加載失敗的腳本進行重新加載。

          重新加載的方案,一般是通過更換域名來解決。我們給每個腳本添加一個映射關系表,用來在加載失敗時匹配新的域名進行重試。

          具體的解決方案,下面我一步一步講解,另外希望大家可以仔細閱讀注釋中的內容

          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>腳本加載失敗如何重試</title>
              <script>
                window.addEventListener(
                  "error", // 監聽全局錯誤
                  function (e) {
                    console.log(e);
                  },
                  true // 由于腳本加載失敗不會冒泡,所以我們要在捕獲階段進行監聽
                );
              </script>
            </head>
            <body>
              <script src="https://www.zowlsat.com/api/1.js"></script>
              <script src="https://www.qqqqqqq.com/api/2.js"></script>
              <script src="https://www.zowlsat.com/api/3.js"></script>
            </body>
          </html>

          此時我們可以在瀏覽器控制臺看到以下效果

          但是這個監聽方法會監聽到很多其他的錯誤,我們只需要監聽腳本加載失敗的錯誤,所以我們要通過這個監聽事件的參數 e 來判斷了

          根據上圖我們可以發現,普通錯誤的類型是 ErrorEvent,而腳本加載失敗的類型是 Event,并且他的 target 會指向 script 標簽,所以我們根據這個區別過濾掉其他的錯誤,這樣剩下的情況才是我們需要處理的。

          window.addEventListener(
            "error",
            function (e) {
              if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) return;
              console.log(e);
            },
            true
          );

          接下來就是如何來實現重新加載,我們先給需要重新加載的域名建立一個映射關系,用于替換映射關系表中的域名。然后就是挨個匹配,當還是加載失敗時繼續匹配下一個,直到成功為止。

          const domainList = ["www.aaaaa.com", "www.bbbbb.com", "www.zowlsat.com"];
          const retry = {};
          window.addEventListener(
            "error",
            function (e) {
              if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) return;
              // 創建一個URL對象
              const url = new URL(e.target.src);
              // 獲取文件路徑
              const key = url.pathname;
              // 假如映射表中沒有這個文件路徑,那么就初始化一個映射鍵
              if (!(key in retry)) {
                retry[key] = 0;
              }
              // 假如匹配完整個映射表都沒重新加載成功,則放棄
              const index = retry[key];
              if (index >= domainList.length) {
                return;
              }
              // 獲取新的完整路徑
              const domain = domainList[index];
              // 替換域名
              url.host = domain;
              // 創建新的script標簽
              const script = document.createElement("script");
              script.src = url.toString();
              // 將新的script標簽追加到加載失敗的script標簽之前
              document.body.insertBefore(script, e.target);
              retry[key]++;
            },
            true // 由于腳本加載失敗不會冒泡,所以我們要在捕獲階段進行監聽
          );

          到此為止,我們功能已經基本實現,效果如下圖

          但是有一個很關鍵的問題,就是假如我 2.js 這個文件中的內容,在 3.js 中要使用,那這樣的話,2.js 就必須加載到 3.js 之前,否則就會報錯。此時,我們就需要在 2.js 加載失敗時,阻塞瀏覽器的解析,知道重新加載完成或者放棄重新加載時,再繼續渲染之后的內容。

          那這樣的話我們該怎么做呢?


          其實很簡單,在我們入門 js 時就學到過一個知識點,就是使用document.write

          document.write這個方法在解析期間使用的話,會阻塞瀏覽器的解析,而我們現在就是需要阻塞瀏覽器解析,那此時我們只需要將創建 script 標簽的方法更換為document.write方法即可。


          修改之后的代碼如下:

          const domainList = ["www.aaaaa.com", "www.bbbbb.com", "www.zowlsat.com"];
          const retry = {};
          window.addEventListener(
            "error",
            function (e) {
              if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) return;
              const url = new URL(e.target.src);
              const key = url.pathname;
              if (!(key in retry)) {
                retry[key] = 0;
              }
              const index = retry[key];
              if (index >= domainList.length) {
                return;
              }
              const domain = domainList[index];
              url.host = domain;
              // 此處加上轉譯是因為防止編譯器識別script標簽為結束標簽報錯
              document.write(`\<script src="${url.toString()}">\<\/script>`);
              //   const script = document.createElement("script");
              //   script.src = url.toString();
              //   document.body.insertBefore(script, e.target);
              retry[key]++;
            },
            true
          );

          現在我們再打開控制臺查看,現在js文件按它原來的順序執行了,這樣既不會改變原有的代碼邏輯,又可以在可控范圍內進行重新加載。

          效果如下圖:

          以上是簡單實現了一個js文件重新加載錯誤的方案,其實這個方案也可以運用到其他很多類型的文件,不限于js文件。

          然后我們還需要更加細化這個方法的話,我們可能還需要考慮到這個script標簽是否帶有async、defer等屬性,還有諸多需要考慮的點,但是沿著這個方向解決的話,大體是沒有問題的。

          關于我們

          本文由ChatMoney團隊出品,ChatMoney專注于AI應用落地與變現,我們提供全套、持續更新的AI源碼系統與可執行的變現方案,致力于幫助更多人利用AI來變現,歡迎進入ChatMoney獲取更多AI變現方案!

          SS加載確實有可能阻塞頁面加載,但這并非絕對,具體取決于CSS的加載方式、應用位置以及瀏覽器的渲染機制。在了解CSS加載如何影響頁面加載之前,我們先要明白瀏覽器渲染頁面的基本流程。

          瀏覽器在加載網頁時,會按照從上到下的順序解析HTML文檔。當瀏覽器遇到`<link>`標簽引用外部CSS文件時,它會停止HTML的解析,轉而加載并應用這個CSS文件。這個過程被稱為CSS阻塞。因此,如果這個CSS文件很大或者加載速度很慢,用戶可能會看到一個空白頁面,直到CSS文件完全加載并應用。

          然而,有幾種方法可以避免或減輕CSS加載對頁面加載的阻塞:

          1. 異步加載CSS:通過將CSS文件的加載設置為異步,可以確保HTML解析不會被阻塞。這可以通過在`<link>`標簽中添加`rel="async"`屬性來實現。這樣,瀏覽器會在后臺加載CSS文件,而不會停止HTML的解析。
          2. 內聯CSS:將CSS代碼直接寫在HTML文件中,而不是通過外部文件引用,可以避免網絡請求造成的延遲。但是,這會增加HTML文件的大小,可能導致其他性能問題。
          3. 使用CSS-in-JS庫:一些庫,如Styled Components或Emotion,允許你在JavaScript中編寫CSS。這種方法可以動態生成樣式,但也可能增加JavaScript的復雜性。
          4. 分割CSS:將CSS文件分割成多個小文件,每個文件只包含一部分樣式。這樣,即使某個文件加載較慢,也不會阻塞整個頁面的渲染。
          5. 利用媒體查詢:通過媒體查詢,你可以根據設備的特性(如屏幕大小、分辨率等)加載不同的CSS文件。這樣,用戶只會下載并應用他們真正需要的樣式。
          6. 預加載和預獲取:使用`<link rel="preload">`和`<link rel="prefetch">`可以告訴瀏覽器提前加載CSS文件。雖然這并不能阻止CSS阻塞,但它可以確保文件在需要時立即可用。

          此外,值得注意的是,現代瀏覽器通常具有一些優化機制,如并行下載、緩存等,這些都可以幫助減少CSS加載對頁面加載的影響。

          總的來說,CSS加載確實有可能阻塞頁面加載,但通過一些優化策略和技術,我們可以減輕或避免這種阻塞。選擇哪種策略取決于你的具體需求和約束。


          主站蜘蛛池模板: 国产在线精品一区二区在线看| 韩国精品一区二区三区无码视频 | 久久毛片一区二区| 亚洲一区二区三区国产精品无码| 爱爱帝国亚洲一区二区三区| 国产精品免费大片一区二区| 精品无码一区二区三区爱欲九九| 白丝爆浆18禁一区二区三区| 亚洲视频一区在线| 久久99热狠狠色精品一区| 日韩精品一区二区三区中文3d| 色窝窝无码一区二区三区| 久久精品国产第一区二区| 国产精品一区二区三区久久| 亚洲AV无码一区二区三区人| 国产精品特级毛片一区二区三区| 亚洲国产精品一区二区三区久久 | 亚洲一区综合在线播放| 国产福利电影一区二区三区久久久久成人精品综合 | 亚洲视频一区在线观看| 亚洲线精品一区二区三区| 一区二区三区四区在线视频| 久久久久人妻精品一区三寸| 精品国产伦一区二区三区在线观看| 精品久久一区二区| 国语精品一区二区三区| 好吊视频一区二区三区| 一级特黄性色生活片一区二区| 国产精品一区二区无线| 免费看无码自慰一区二区| 亚洲日韩国产一区二区三区在线 | 内射少妇一区27P| 影院成人区精品一区二区婷婷丽春院影视 | 久久久久人妻一区二区三区vr| 国产一区二区视频在线播放| 亚洲男人的天堂一区二区| 一区二区三区午夜| 波多野结衣高清一区二区三区| 亚洲乱码国产一区网址| 精品日本一区二区三区在线观看| 无码日韩人妻AV一区二区三区|