成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

首屏?xí)r間,你說你優(yōu)化了,那你倒是計(jì)算出來給我看啊!

開發(fā) 前端
我們只需要知道瀏覽器開始顯示內(nèi)容的時(shí)間點(diǎn),即頁面白屏結(jié)束時(shí)間點(diǎn)即可獲取到頁面的白屏?xí)r間。因此,我們通常認(rèn)為瀏覽器開始渲染<body>標(biāo)簽或者解析完<head>標(biāo)簽的時(shí)刻就是頁面白屏結(jié)束的時(shí)間點(diǎn)。

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點(diǎn)是我的座右銘,基礎(chǔ)是進(jìn)階的前提是我的初心

背景

當(dāng)我們在做項(xiàng)目的性能優(yōu)化的時(shí)候,優(yōu)化首屏?xí)r間是一個(gè)避不過去的優(yōu)化方向,但是又有多少人想過這兩個(gè)東西的區(qū)別呢:

  • 白屏?xí)r間
  • 首屏?xí)r間

并且這兩個(gè)時(shí)間的計(jì)算方式又有什么區(qū)別呢?接下來我就給大家講一下吧!

白屏?xí)r間

是什么?

白屏?xí)r間指的是:頁面開始顯示內(nèi)容的時(shí)間。也就是:瀏覽器顯示第一個(gè)字符或者元素的時(shí)間

圖片圖片

怎么算?

我們只需要知道瀏覽器開始顯示內(nèi)容的時(shí)間點(diǎn),即頁面白屏結(jié)束時(shí)間點(diǎn)即可獲取到頁面的白屏?xí)r間。

因此,我們通常認(rèn)為瀏覽器開始渲染<body>標(biāo)簽或者解析完<head>標(biāo)簽的時(shí)刻就是頁面白屏結(jié)束的時(shí)間點(diǎn)。

  • 瀏覽器支持performance.timing
<head>
  <title>Document</title>
</head>
<script type="text/javascript">
  // 白屏?xí)r間結(jié)束點(diǎn)
  var firstPaint = Date.now()
  var start = performance.timing.navigationStart
  console.log(firstPaint - start)
</script>
  • 瀏覽器不支持performance.timing
<head>
  <title>Document</title>
  <script type="text/javascript">
    window.start = Date.now();
  </script>
</head>
<script type="text/javascript">
  // 白屏?xí)r間結(jié)束點(diǎn)
  var firstPaint = Date.now()
  console.log(firstPaint - window.start)
</script>

首屏?xí)r間

是什么?

首屏?xí)r間是指用戶打開網(wǎng)站開始,到瀏覽器首屏內(nèi)容渲染完成的時(shí)間。對于用戶體驗(yàn)來說,首屏?xí)r間是用戶對一個(gè)網(wǎng)站的重要體驗(yàn)因素。

圖片圖片

為什么不直接用生命周期?

有些小伙伴會說:為啥不直接在App.vue的mounted生命周期里計(jì)算時(shí)間呢?大家可以看看,官網(wǎng)說了mounted執(zhí)行并不代表首屏所有元素加載完畢,所以mounted計(jì)算出來的時(shí)間會偏短。

圖片圖片

為什么不直接用nextTick?

nextTick回調(diào)的時(shí)候,首屏的DOM都渲染出來了,但是計(jì)算首屏?xí)r間并不需要渲染所有DOM,所以計(jì)算出來的時(shí)間會偏長

怎么算?

我們需要利用MutationObserver監(jiān)控DOM的變化,監(jiān)控每一次DOM變化的分?jǐn)?shù),計(jì)算的規(guī)則為: (1 + 層數(shù) * 0.5),我舉個(gè)例子:

<body>
    <div>
      <div>1</div>
      <div>2</div>
    </div>
</body>

以上DOM結(jié)構(gòu)的分?jǐn)?shù)為:

1.5 + 2 + 2.5 + 2.5 = 8.5(分)

圖片圖片

其實(shí)在首屏的加載中,會涉及到DOM的增加、修改、刪除,所以會觸發(fā)多次MutationObserver,所以會統(tǒng)計(jì)出不同階段的score,我們把這些score存放在一個(gè)數(shù)組observerData中,后面大有用處

首屏?xí)r間實(shí)踐

現(xiàn)在我們開始計(jì)算首屏?xí)r間吧!

前置準(zhǔn)備

  • index.html:html頁面
<!DOCTYPE html>
<html lang="en">
  <head> </head>
  <body>
    <div>
      <div>
        <div>1</div>
        <div>2</div>
      </div>
      <div>3</div>
      <div>4</div>
    </div>
    <ul id="ulbox"></ul>
  </body>
  <script src="./computed.js"></script>
  <script src="./request.js"></script>
</html>
  • computed.js:計(jì)算首屏?xí)r間的文件
const observerData = []

let observer = new MutationObserver(() => {
  // 計(jì)算每次DOM修改時(shí),距離頁面剛開始加載的時(shí)間
  const start = window.performance.timing.navigationStart
  const time = new Date().getTime() - start
  
  const body = document.querySelector('body')
  const score = computedScore(body, 1)
  // 加到數(shù)組 observerData 中
  observerData.push({
    score,
    time
  })
})
observer.observe(
  document, {
    childList: true,
    subtree: true
  }
)

function computedScore(element, layer) {
  let score = 0
  const tagName = element.tagName
  // 排除這些標(biāo)簽的情況
  if (
    tagName !== 'SCRIPT' &&
    tagName !== 'STYLE' &&
    tagName !== 'META' &&
    tagName !== 'HEAD'
  ) {
    const children = element.children
    if (children && children.length) {
      // 遞歸計(jì)算分?jǐn)?shù)
      for (let i = 0; i < children.length; i++) {
        score += computedScore(children[i], layer + 1)
      }
    }

    score += 1 + 0.5 * layer
  }
  return score
}
  • request.js:模擬請求修改DOM
// 模擬請求列表
const requestList = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(
        [1, 2, 3,
          4, 5, 6,
          7, 8, 9
        ]
      )
    }, 1000)
  })
}

const ulbox = document.getElementById('ulbox')

// 模擬請求數(shù)據(jù)渲染列表
const renderList = async () => {
  const list = await requestList()
  const fragment = document.createDocumentFragment()
  for (let i = 0; i < list.length; i++) {
    const li = document.createElement('li')
    li.innerText = list[i]
    fragment.appendChild(li)
  }
  ulbox.appendChild(fragment)
}

// 模擬對列表進(jìn)行輕微修改
const addList = async () => {
  const li = document.createElement('li')
  li.innerText = '加上去'
  ulbox.appendChild(li)
}

(async () => {
  // 模擬請求數(shù)據(jù)渲染列表
  await renderList()
  // 模擬對列表進(jìn)行輕微修改
  addList()
})()

observerData

當(dāng)我們一切準(zhǔn)備就緒后運(yùn)行代碼,我們獲得了observerData,我們看看它長什么樣?

計(jì)算首屏?xí)r間

我們怎么根據(jù)observerData來計(jì)算首屏?xí)r間呢?我們可以這么算:下次分?jǐn)?shù)比上次分?jǐn)?shù)增加幅度最大的時(shí)間作為首屏?xí)r間

很多人會問了,為什么不是取最后一項(xiàng)的時(shí)間來當(dāng)做首屏?xí)r間呢?大家要注意了:首屏并不是所有DOM都渲染,我就拿剛剛的代碼來舉例吧,我們渲染完了列表,然后再去增加一個(gè)li,那你是覺得哪個(gè)時(shí)間段算是首屏呢?應(yīng)該是渲染完列表后算首屏完成,因?yàn)楹竺嬷辉黾恿艘粋€(gè)li,分?jǐn)?shù)的漲幅較小,可以忽略不計(jì)

所以我們開始計(jì)算吧:

const observerData = []

let observer = new MutationObserver(() => {
  // 計(jì)算每次DOM修改時(shí),距離頁面剛開始加載的時(shí)間
  const start = window.performance.timing.navigationStart
  const time = new Date().getTime() - start
  const body = document.querySelector('body')
  const score = computedScore(body, 1)
  observerData.push({
    score,
    time
  })

  // complete時(shí)去調(diào)用 unmountObserver
  if (document.readyState === 'complete') {
    // 只計(jì)算10秒內(nèi)渲染時(shí)間
    unmountObserver(10000)
  }
})
observer.observe(
  document, {
    childList: true,
    subtree: true
  }
)

function computedScore(element, layer) {
  let score = 0
  const tagName = element.tagName
  // 排除這些標(biāo)簽的情況
  if (
    tagName !== 'SCRIPT' &&
    tagName !== 'STYLE' &&
    tagName !== 'META' &&
    tagName !== 'HEAD'
  ) {
    const children = element.children
    if (children && children.length) {
      // 遞歸計(jì)算分?jǐn)?shù)
      for (let i = 0; i < children.length; i++) {
        score += computedScore(children[i], layer + 1)
      }
    }

    score += 1 + 0.5 * layer
  }
  return score
}

// 計(jì)算首屏?xí)r間
function getFirstScreenTime() {
  let data = null
  for (let i = 1; i < observerData.length; i++) {
    // 計(jì)算幅度
    const differ = observerData[i].score - observerData[i - 1].score
    // 取最大幅度,記錄對應(yīng)時(shí)間
    if (!data || data.rate <= differ) {
      data = {
        time: observerData[i].time,
        rate: differ
      }
    }
  }
  return data
}

let timer = null

function unmountObserver(delay) {
  if (timer) return
  timer = setTimeout(() => {
    // 輸出首屏?xí)r間
    console.log(getFirstScreenTime())
    // 終止MutationObserver的監(jiān)控
    observer.disconnect()
    observer = null
    clearTimeout(timer)
  }, delay)
}

計(jì)算出首屏?xí)r間1020ms

總結(jié)

我這個(gè)計(jì)算方法其實(shí)很多漏洞,沒把刪除元素也考慮進(jìn)去,但是想讓大家知道計(jì)算首屏?xí)r間的計(jì)算思想,這才是最重要的,希望大家能理解這個(gè)計(jì)算思想。

責(zé)任編輯:武曉燕 來源: 前端之神
相關(guān)推薦

2021-08-11 07:55:10

Go內(nèi)置函數(shù)

2022-02-24 07:56:27

Linux系統(tǒng)ELF

2021-02-14 14:06:24

SQL數(shù)據(jù)庫面試

2015-06-25 10:57:15

推薦系統(tǒng)老婆算出來

2009-11-06 13:54:09

Visual Stud

2022-07-14 08:22:48

Computedvue3

2021-11-09 14:08:45

DockerDockerfileJava

2021-03-01 10:38:13

深度學(xué)習(xí)編程人工智能

2011-08-24 13:32:56

CREATE TABL中文man

2020-03-16 10:42:23

大數(shù)據(jù)IT工具

2020-03-25 10:44:16

位運(yùn)算操作技巧

2023-11-08 08:09:36

幾何算法解析幾何

2023-05-08 00:01:29

數(shù)據(jù)分析指標(biāo)標(biāo)簽

2024-12-26 11:49:14

2009-09-10 16:22:48

LINQ建立數(shù)據(jù)報(bào)表

2023-12-08 13:16:00

CSSJSXStyleX

2022-10-12 00:07:25

加密貨幣區(qū)塊鏈比特幣

2012-04-25 22:58:36

2018-03-19 10:39:28

Java序列化對象

2018-12-12 11:11:20

系統(tǒng)可靠性可用性
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 一区二区三区四区在线免费观看 | 又黄又爽的网站 | 欧美午夜精品 | 岛国av免费观看 | 国产精品毛片 | 国产精品久久久久久一区二区三区 | 日日操av | 欧美涩涩网 | 国产精品99久久久久久宅男 | 岛国视频 | 国产精品视频入口 | 天堂久久网 | 精品国产乱码久久久久久久久 | 免费在线观看一区二区 | 精品国产一区二区三区久久 | 亚洲欧美一区二区在线观看 | 韩日在线视频 | 国产精品一区二区久久 | 国产成人免费视频 | 久久com | 精品国产18久久久久久二百 | www精品美女久久久tv | 人人干人人超 | 欧美激情精品久久久久久变态 | 久久精品视频一区二区三区 | 91久久精品国产免费一区 | 亚洲免费在线 | 亚洲国产精品人人爽夜夜爽 | 亚洲激情第一页 | 亚洲精品在线视频 | 国产亚洲精品一区二区三区 | 午夜精品 | 日韩一区在线播放 | 天天干天天爱天天爽 | 日本a网站 | 亚洲九九 | 欧美日韩三级视频 | av在线一区二区三区 | 狠狠干av | 国产黄色网址在线观看 | 国产日韩一区二区三区 |