

首屏时间,你说你优化了,那你倒是计算出给给我看啊!
source link: https://segmentfault.com/a/1190000041419030
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

首屏时间,你说你优化了,那你倒是计算出给给我看啊!
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心
当我们在做项目的性能优化的时候,优化首屏时间是一个避不过去的优化方向,但是又有多少人想过这两个东西的区别呢:
白屏时间
首屏时间
并且这两个时间的计算方式又有什么区别呢?接下来我就给大家讲一下吧!
白屏时间指的是:页面开始显示内容的时间。也就是:浏览器显示第一个字符或者元素的时间
我们只需要知道浏览器开始显示内容的时间点,即页面白屏结束时间点即可获取到页面的白屏时间。
因此,我们通常认为浏览器开始渲染 <body>
标签或者解析完 <head>
标签的时刻就是页面白屏结束的时间点。
浏览器支持
performance.timing
<head> <title>Document</title> </head> <script type="text/javascript"> // 白屏时间结束点 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"> // 白屏时间结束点 var firstPaint = Date.now() console.log(firstPaint - window.start) </script>
首屏时间是指用户打开网站开始,到浏览器首屏内容渲染完成的时间。对于用户体验来说,首屏时间是用户对一个网站的重要体验因素。
为什么不直接用生命周期?
有些小伙伴会说:为啥不直接在App.vue的 mounted
生命周期里计算时间呢?大家可以看看,官网说了 mounted
执行并不代表首屏所有元素加载完毕,所以 mounted
计算出来的时间会偏短。
为什么不直接用nextTick?
nextTick
回调的时候,首屏的DOM都渲染出来了,但是计算 首屏时间
并不需要渲染所有DOM,所以计算出来的时间会偏长
我们需要利用 MutationObserver
监控DOM的变化,监控每一次DOM变化的分数,计算的规则为:
(1 + 层数 * 0.5),我举个例子:
<body> <div> <div>1</div> <div>2</div> </div> </body>
以上DOM结构的分数为:
1.5 + 2 + 2.5 + 2.5 = 8.5(分)
其实在首屏的加载中,会涉及到DOM的增加、修改、删除,所以会触发多次 MutationObserver
,所以会统计出不同阶段的score,我们把这些score存放在一个数组 observerData
中,后面大有用处
首屏时间实践
现在我们开始计算首屏时间吧!
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
:计算首屏时间的文件const observerData = [] let observer = new MutationObserver(() => { // 计算每次DOM修改时,距离页面刚开始加载的时间 const start = window.performance.timing.navigationStart const time = new Date().getTime() - start const body = document.querySelector('body') const score = computedScore(body, 1) // 加到数组 observerData 中 observerData.push({ score, time }) }) observer.observe( document, { childList: true, subtree: true } ) function computedScore(element, layer) { let score = 0 const tagName = element.tagName // 排除这些标签的情况 if ( tagName !== 'SCRIPT' && tagName !== 'STYLE' && tagName !== 'META' && tagName !== 'HEAD' ) { const children = element.children if (children && children.length) { // 递归计算分数 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') // 模拟请求数据渲染列表 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) } // 模拟对列表进行轻微修改 const addList = async () => { const li = document.createElement('li') li.innerText = '加上去' ulbox.appendChild(li) } (async () => { // 模拟请求数据渲染列表 await renderList() // 模拟对列表进行轻微修改 addList() })()
observerData
当我们一切准备就绪后运行代码,我们获得了 observerData
,我们看看它长什么样?
计算首屏时间
我们怎么根据 observerData
来计算首屏时间呢?我们可以这么算:下次分数比上次分数增加幅度最大的时间作为首屏时间
很多人会问了,为什么不是取最后一项的时间来当做首屏时间呢?大家要注意了:首屏并不是所有DOM都渲染,我就拿刚刚的代码来举例吧,我们渲染完了列表,然后再去增加一个li,那你是觉得哪个时间段算是首屏呢?应该是渲染完列表后算首屏完成,因为后面只增加了一个li,分数的涨幅较小,可以忽略不计
所以我们开始计算吧:
const observerData = [] let observer = new MutationObserver(() => { // 计算每次DOM修改时,距离页面刚开始加载的时间 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时去调用 unmountObserver if (document.readyState === 'complete') { // 只计算10秒内渲染时间 unmountObserver(10000) } }) observer.observe( document, { childList: true, subtree: true } ) function computedScore(element, layer) { let score = 0 const tagName = element.tagName // 排除这些标签的情况 if ( tagName !== 'SCRIPT' && tagName !== 'STYLE' && tagName !== 'META' && tagName !== 'HEAD' ) { const children = element.children if (children && children.length) { // 递归计算分数 for (let i = 0; i < children.length; i++) { score += computedScore(children[i], layer + 1) } } score += 1 + 0.5 * layer } return score } // 计算首屏时间 function getFirstScreenTime() { let data = null for (let i = 1; i < observerData.length; i++) { // 计算幅度 const differ = observerData[i].score - observerData[i - 1].score // 取最大幅度,记录对应时间 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(() => { // 输出首屏时间 console.log(getFirstScreenTime()) // 终止MutationObserver的监控 observer.disconnect() observer = null clearTimeout(timer) }, delay) }
计算出首屏时间 1020ms
我这个计算方法其实很多漏洞,没把删除元素也考虑进去,但是想让大家知道计算首屏时间的计算思想,这才是最重要的,希望大家能理解这个计算思想
我是林三心,一个热心的前端菜鸟程序员。如果你上进,喜欢前端,想学习前端,那咱们可以交朋友,一起摸鱼哈哈,摸鱼群,加我请备注【思否】
Recommend
-
290
Vue SPA 首屏加载优化实践 2017年12月08日 02:32 · 阅读 27087
-
68
背景随着公司业务的不断壮大,最近老是有用户反应公司APP内的商城打开比较慢,这可不行啊,慢了容易流失用户,流失用户减少公司业绩,公司业绩少我的年终奖就少…………,所以为了公司,也为了自己,开始优化之路。商城系统是去年开发的,是一个基于vue2.0的spa项目,
-
60
作者:江敏熙 贝聊前端开发工程师 前言 我司的官网首页——贝聊官网,首屏有一个自动播放的背景视频,一直被诟病视频加载慢、播放卡。刚开始以为是文件太大,或者是网速太慢,但当我去优化它的时候,发现并没有预想的简单。本文记录了优化过程和经验总结,希望能对读...
-
78
-
84
提取第三方库,缓存,减少打包体积 1、 dll动态链接库, 使用DllPlugin DllReferencePlugin,将第三方库提取出来另外打包出来,然后动态引入html。可以提高打包速度和缓存第三方库 这种方式打包可以见京东团队的gaea方案 ww
-
54
https://segmentfault.com/a/1190000019499007 之前用 vuecli 做了个博客,是一个单页面项目,大概有十个路由 直接 npm run build 打包出来,有一个
-
16
对构建的改造已经完成,目前构建的能力可以较为灵活地支撑进一步的优化 希望进一步减少首屏时间,将首屏时间控制在2秒以内 页面情况: 优化之前,并没有上报首屏时间,页面加载时间约为2.4秒。 研究认...
-
10
前言 前端页面性能对用户留存、用户直观体验有着重要作用。这样的话如何更好的监控前端页面性能就变的十分重要。...
-
9
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心,众所周知哈, Promise 在咱们的开发中是相当的重要,我觉得对于 Promise 的使用等级,可以分为三...
-
5
我们知道,用户体验是 Web 产品最为重要的部分。尽可能减少首屏加载时间,更...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK