自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

前言

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

背景

當(dāng)我們?cè)谧鲰?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間指的是:頁(yè)面開始顯示內(nèi)容的時(shí)間。也就是:瀏覽器顯示第一個(gè)字符或者元素的時(shí)間

圖片圖片

怎么算?

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

因此,我們通常認(rèn)為瀏覽器開始渲染<body>標(biāo)簽或者解析完<head>標(biāo)簽的時(shí)刻就是頁(yè)面白屏結(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í)間。對(duì)于用戶體驗(yàn)來說,首屏?xí)r間是用戶對(duì)一個(gè)網(wǎng)站的重要體驗(yàn)因素。

圖片圖片

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

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

圖片圖片

為什么不直接用nextTick?

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

怎么算?

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

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

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

前置準(zhǔn)備

  • index.html:html頁(yè)面
<!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í),距離頁(yè)面剛開始加載的時(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:模擬請(qǐng)求修改DOM
// 模擬請(qǐng)求列表
const requestList = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(
        [1, 2, 3,
          4, 5, 6,
          7, 8, 9
        ]
      )
    }, 1000)
  })
}

const ulbox = document.getElementById('ulbox')

// 模擬請(qǐng)求數(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)
}

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

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

observerData

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

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

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

很多人會(huì)問了,為什么不是取最后一項(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í),距離頁(yè)面剛開始加載的時(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
    // 取最大幅度,記錄對(duì)應(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

2015-06-25 10:57:15

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

2021-02-14 14:06:24

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

2022-07-14 08:22:48

Computedvue3

2009-11-06 13:54:09

Visual Stud

2021-11-09 14:08:45

DockerDockerfileJava

2021-03-01 10:38:13

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

2020-03-16 10:42:23

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

2020-03-25 10:44:16

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

2011-08-24 13:32:56

CREATE TABL中文man

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)表

2022-10-12 00:07:25

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

2023-12-08 13:16:00

CSSJSXStyleX

2012-04-25 22:58:36

2018-10-29 11:25:38

云計(jì)算行業(yè)科技

2016-11-18 09:37:07

EC2,云計(jì)算故障賠償
點(diǎn)贊
收藏

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