<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【瀏覽器渲染原理系列】這是一份關(guān)于加載script和style的實(shí)驗(yàn)報(bào)告

          共 11852字,需瀏覽 24分鐘

           ·

          2021-07-07 11:07

          ?

          如果做性能優(yōu)化,一定會(huì)想當(dāng)?shù)囊粋€(gè)優(yōu)化點(diǎn)就是script標(biāo)簽和link標(biāo)簽要放置的位置,當(dāng)然大部分的觀點(diǎn)都是script標(biāo)簽放到</body>之前,link標(biāo)簽放到title中;或者是配合async、defer、preload、prefech使用,當(dāng)然目的只有一個(gè):讓頁(yè)面盡可能快的展示在用戶面前。下面僅僅會(huì)討論瀏覽器獲取到HTML文件后的部分。

          ?

          瀏覽器渲染過(guò)程

          ?

          瀏覽器獲取到HTML文件后,開(kāi)始渲染工作。這里以webkit引擎為例。

          1. 解析html產(chǎn)生DOM樹(shù)
          2. 解析css樣式產(chǎn)生CSSOM樹(shù)
          3. DOM樹(shù)和CSSOM樹(shù)合成渲染樹(shù)(RenderTree)
          4. 布局RenderTree(layout):確定在屏幕中位置
          5. 繪制(paint)
          6. 合成(composite): 將多個(gè)圖層合并

          為了提高用戶體驗(yàn),渲染引擎會(huì)盡快的把結(jié)果渲染給用戶,它不會(huì)等待所有的html都解析完成才渲染,它會(huì)在網(wǎng)絡(luò)層獲取文檔的同時(shí),將已經(jīng)接收的局部渲染到頁(yè)面中(實(shí)驗(yàn)4將會(huì)證明這一說(shuō)法)

          ?

          實(shí)驗(yàn)環(huán)境準(zhǔn)備

          1. 模擬服務(wù)端: 所需要的文件hand.js、style.css、server.js

          // server.js
          const http = require('http');
          const fs = require('fs')

          http.createServer(function (request, response{

            if (request.url === '/index.html') {
              fs.readFile('./index.html', (err, data) => {
                response.setHeader('Content-Type''text/html');
                if (!err) {
                    response.end(data);
                }else {
                    response.end('html not found');
                }
              })
            }

            if (request.url === '/hand.js') {
              fs.readFile('./static/hand.js', (err, data) => {
                response.setHeader('Content-Type''text/javascript');
                if (!err) {
                    setTimeout(() => {
                      response.end(data);
                    }, 100);
                }else {
                    response.end('html not found');
                }
              })
            }

            if (request.url === '/style.css') {
              fs.readFile('./static/style.css', (err, data) => {
                response.setHeader('Content-Type''text/css');
                if (!err) {
                    setTimeout(() => {
                      response.end(data);
                    }, 1000);
                }else {
                    response.end('html not found');
                }
              })
            }

            if (request.url === '/favicon.ico') {
              response.end()
            }

          }).listen(8888);

          console.log('port 8888')

          // hand.js(無(wú)內(nèi)容)
          // style.css
          p {
            color: red;
          }

          前端部分

          <!--index.html-->
          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>
          </html>

          備注:以下的實(shí)驗(yàn)圖片中,請(qǐng)忽略掉實(shí)驗(yàn)1、3、8、9、10報(bào)告的第一個(gè)渲染幀(因?yàn)闉g覽器會(huì)紀(jì)錄頁(yè)面刷新前的一個(gè)渲染幀)

          script標(biāo)簽

          為什么script標(biāo)簽會(huì)阻塞頁(yè)面的渲染?

          ?

          javascript能操作dom樹(shù),瀏覽器卻不知道腳本中是否有操作dom的代碼(比如document.write等),所以以最壞的打算來(lái)處理:停止dom的解析,所以更準(zhǔn)確的說(shuō)是「script標(biāo)簽會(huì)阻止dom的解析」

          解釋幾個(gè)下面實(shí)驗(yàn)用到的名詞(以下解釋均來(lái)源于MDN)

          1. DCL(DOMContentLoaded): 當(dāng)HTML被完全加載以及「解析」時(shí),DOMContentLoaded事件會(huì)被觸發(fā),而不必等待樣式表,圖片或者子框架完成加載

          2. L(load): 當(dāng)整個(gè)頁(yè)面及所有依賴資源如「樣式表和圖片都已完成加載」時(shí),將觸發(fā)load事件

          3. FP(first paint): 頁(yè)面導(dǎo)航與瀏覽器將該網(wǎng)頁(yè)的第一個(gè)像素「渲染」到屏幕上

          以上名詞縮寫(xiě)將會(huì)出現(xiàn)在下面的實(shí)驗(yàn)截圖中

          ?

          實(shí)驗(yàn)1: 內(nèi)聯(lián)script標(biāo)簽

          <head>
            <meta charset="UTF-8">
            <script>
              var a = 0
              for (let i = 0; i< 1000000000; i++) {
                a += 1
              }
            
          </script>
          </head>
          <body>
            <p>我是第一個(gè)</p>
          </body>
          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <script>
              var a = 0
              for (let i = 0; i< 1000000000; i++) {
                a += 1
              }
            
          </script>
          </body>

          實(shí)驗(yàn)2: 內(nèi)聯(lián)script標(biāo)簽(換一種實(shí)現(xiàn)方式)

          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <script>
              const node = document.getElementsByTagName('p')
              console.log(node[0]) // undefined
            
          </script>
            <p>我是第一個(gè)</p>
          </body>

          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <script>
              const node = document.getElementsByTagName('p')
              console.log(node[0]) // <p>我是第一個(gè)</p>
            
          </script>
          </body>

          實(shí)驗(yàn)3: 外部引入的script標(biāo)簽放在title中

          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <script src="http://localhost:8888/hand.js"></script>
          </head>
          <body>
            <p>我是第一個(gè)</p>
          </body>
          </html>

          實(shí)驗(yàn)4: 外部引入的script標(biāo)簽放在body中

          <html lang="en">
          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <script src="http://localhost:8888/hand.js"></script>
            <p>我是第三個(gè)</p>
          </body>
          </html>

          實(shí)驗(yàn)5: 外部引入的script標(biāo)簽放在</body>前

          <html lang="en">
          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
            <script src="http://localhost:8888/hand.js"></script>
          </body>
          </html>

          實(shí)驗(yàn)6: 外部引入的script標(biāo)簽放在title中,并且加入async參數(shù)

          <head>
            <meta charset="UTF-8">
            <script src="http://localhost:8888/hand.js" async></script>
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>

          實(shí)驗(yàn)7: 外部引入的script標(biāo)簽放在title中,并且加入defer參數(shù)

          <head>
            <meta charset="UTF-8">
            <script src="http://localhost:8888/hand.js" defer></script>
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>
          ?

          總結(jié):

          1. 內(nèi)聯(lián)的script會(huì)阻塞dom解析,并且不會(huì)使之前解析過(guò)的dom預(yù)先渲染
          2. 外部引入的script標(biāo)簽會(huì)阻塞dom的解析,但是之前解析過(guò)的dom瀏覽器會(huì)先渲染
          3. 加入async和defer可以強(qiáng)制script標(biāo)簽不去阻塞dom的解析
          4. defer會(huì)阻塞DOMContentLoaded事件,而async不會(huì)
          ?

          link標(biāo)簽引入的css

          ?

          我們一般用link標(biāo)簽引用css樣式文件。如果你看過(guò)vue打包后的文件,會(huì)發(fā)現(xiàn)它的一些腳本文件也是通過(guò)link標(biāo)簽引入的。不過(guò)我們這篇文章中不對(duì)其進(jìn)行討論。「樣式文件不會(huì)阻塞dom的解析,但是會(huì)阻塞dom的渲染」,接下來(lái)用幾個(gè)實(shí)驗(yàn)來(lái)證明css是如何阻塞dom的渲染的

          ?

          實(shí)驗(yàn)8: link標(biāo)簽引入的css放到title中

          <head>
            <meta charset="UTF-8">
            <link rel="stylesheet" href="http://localhost:8888/style.css">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>

          實(shí)驗(yàn)9: link標(biāo)簽引入的css放到</body>前

          <head>
            <meta charset="UTF-8">
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
            <link rel="stylesheet" href="http://localhost:8888/style.css">
          </body>
          ?

          總結(jié):

          1. css不會(huì)阻塞dom的解析,但是會(huì)阻塞dom的渲染
          2. css若放在body末尾,頁(yè)面會(huì)從無(wú)樣式到有樣式的過(guò)渡,會(huì)讓用戶體驗(yàn)很差
          ?

          script與css的標(biāo)簽同時(shí)存在

          ?

          一個(gè)靜態(tài)文件中一定會(huì)同時(shí)存在script和link標(biāo)簽的情況,它們之間又是互相影響的?

          ?

          實(shí)驗(yàn)10: link與script都放在title中,且link放在script之前

          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <link rel="stylesheet" href="http://localhost:8888/style.css" />
            <script src="http://localhost:8888/hand.js"></script>
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>

          實(shí)驗(yàn)11: link與script都放在title中,且link放在script之后

          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <script src="http://localhost:8888/hand.js"></script>
            <link rel="stylesheet" href="http://localhost:8888/style.css" />
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
          </body>

          實(shí)驗(yàn)12: link在title中,script放到</body>前(情況1)

          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <link rel="stylesheet" href="http://localhost:8888/style.css" />
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
            <script src="http://localhost:8888/hand.js"></script>
          </body>

          實(shí)驗(yàn)13: link在title中,script放到</body>前(情況2)

          // 讓我們修改一下serve.js文件中的js文件和css的返回時(shí)間
          if (request.url === '/hand.js') {
              fs.readFile('./static/hand.js', (err, data) => {
                response.setHeader('Content-Type''text/javascript');
                if (!err) {
                    setTimeout(() => {
                      response.end(data);
                    }, 1000);
                }else {
                    response.end('html not found');
                }
              })
            }

            if (request.url === '/style.css') {
              fs.readFile('./static/style.css', (err, data) => {
                response.setHeader('Content-Type''text/css');
                if (!err) {
                    setTimeout(() => {
                      response.end(data);
                    }, 100);
                }else {
                    response.end('html not found');
                }
              })
            }
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <link rel="stylesheet" href="http://localhost:8888/style.css" />
          </head>
          <body>
            <p>我是第一個(gè)</p>
            <p>我是第二個(gè)</p>
            <p>我是第三個(gè)</p>
            <script src="http://localhost:8888/hand.js"></script>
          </body>
          ?

          總結(jié):

          1. css不會(huì)阻塞外部腳本的加載,但是會(huì)阻塞js的執(zhí)行(GUI線程和js線程互斥,因?yàn)橛锌赡躩s會(huì)操作 CSS)
          2. 最佳實(shí)踐:script標(biāo)簽放在body的末尾,style標(biāo)簽放在body之前
          ?

          參考文檔

          • https://developers.google.cn/web/fundamentals/performance/critical-rendering-path
          • https://developers.google.cn/web/fundamentals/performance/rendering

          瀏覽 64
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲AV蜜桃永久无码精品色哟 | 中文字幕av一区二区三区 | 中文字幕高清无码在线视频 | 青娱乐在线视频免费观看 | 西西444WWW无码高清视频 |