前端vite+vue3——可视化页面性能耗时指标(fmp、fp)

文章目录

    • ⭐前言
      • 💖vue3系列文章
    • ⭐可视化fmp、fp指标
      • 💖 MutationObserver 计算 dom的变化
      • 💖 使用条形图展示 fmp、fp时间
    • ⭐项目代码
    • ⭐结束

yma16-logo

⭐前言

大家好,我是yma16,本文分享关于 前端vite+vue3——可视化页面性能耗时(fmp、fp)。

fmp的定义

FMP(First Meaningful Paint)是一种衡量网页加载性能的指标。它表示在加载过程中,浏览器首次渲染出有意义的内容所花费的时间。有意义的内容指的是用户可以看到和交互的元素,如文本、图片、按钮等。

首次渲染的定义可以根据具体的要求和场景而有所不同。通常情况下,首次渲染是指在页面加载过程中,浏览器首次绘制出用户能够理解和识别的内容,而不是空白页面或加载指示符。

FMP的计算方法可以根据不同的标准和工具而有所差异,但通常会考虑页面上可见的内容和用户可交互的元素。在计算FMP时,一般会排除一些延迟加载的元素,如懒加载的图片或动态加载的内容,以确保测量的是真正有意义的渲染时间。

fp的定义

FP(First Paint)是指浏览器首次将像素渲染到屏幕上的时间点

传统性能指标 Performance
Performance 接口可以获取到当前页面中与性能相关的信息
performance

performance 参数解析
domLoading: 浏览器即将开始解析第一批收到的 HTML 文档字节,Document.readyState 变为 loading。

domInteractive: 当前网页 DOM结构结束解析、开始加载内嵌资源的时间点。Document.readyState 变为 interactive。

domContentLoadedEventStart: 当解析器发送 DomContentLoaded 事件,所有需要被执行的脚本已经被解析。

domContentLoadedEventEnd: 所有需要立即执行的脚本已经被执行。

domComplete: 当前文档解析完成, Document.readyState 变为 complete。

loadEventStart: 作为每个网页加载的最后一步,浏览器会触发 load 事件,以便触发额外的应用逻辑。如果这个事件还未被发送,它的值将会是 0。

loadEventEnd:load 事件执行完成。如果这个事件还未被发送,或者尚未完成,它的值将会是 0。

count

💖vue3系列文章

vue3 + fastapi 实现选择目录所有文件自定义上传到服务器
前端vue2、vue3去掉url路由“ # ”号——nginx配置
csdn新星计划vue3+ts+antd赛道——利用inscode搭建vue3(ts)+antd前端模板
认识vite_vue3 初始化项目到打包
python_selenuim获取csdn新星赛道选手所在城市用echarts地图显示
让大模型分析csdn文章质量 —— 提取csdn博客评论在文心一言分析评论区内容
前端vue3——html2canvas给网站截图生成宣传海报
vue3+echarts可视化——记录我的2023编程之旅
前端vite+vue3——自动化配置路由布局

⭐可视化fmp、fp指标

由于vue是SPA(single-page application)单页面项目,会导致首次加载时间长:SPA需要加载整个应用的代码和资源,首次加载时间可能会比传统的多页面应用长。

以下是我个人计算fmp的逻辑

  • fmp:监听 vue挂载的节点(dom的id为root)首次变化时间
  • fp: 监听 beforeMounted的时间为白屏结束时间

计算的单位使用performance.now

performance.now()是一个JavaScript方法,用于获取当前时间戳,精确到毫秒级。它返回一个DOMHighResTimeStamp对象,表示从性能测量器启动到调用performance.now()的时间间隔。这个方法通常用于性能测量和性能优化,可以用于计算代码执行时间、动画帧率、网络请求延迟等。

💖 MutationObserver 计算 dom的变化

MutationObserver 接口提供了监视对 DOM 树所做更改的能力

使用示例:

// 选择需要观察变动的节点
const targetNode = document.getElementById("some-id");

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
  // Use traditional 'for loops' for IE 11
  for (let mutation of mutationsList) {
    if (mutation.type === "childList") {
      console.log("A child node has been added or removed.");
    } else if (mutation.type === "attributes") {
      console.log("The " + mutation.attributeName + " attribute was modified.");
    }
  }
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

在vue的入口页面app.vue编写监听rootDom变化的逻辑
mutationAction 函数如下(将节点的高度大于0作为结束监听的条件即fmp的结束时间)

// 监听 dom变化
mutationAction(listenDom, callbackAction) {
    // 观察器的配置(需要观察什么变动)
    const config = { attributes: true, childList: true, subtree: true };
    // 当观察到变动时执行的回调函数
    const callback = function (mutationsList, observer) {
        // 渲染高度
        const renderHeight = listenDom.offsetHeight
        if (parseInt(renderHeight)) {
            // 第一次监听dom 存在高度则判定已经渲染完root节点
            callbackAction()
            // 停止观察
            observer.disconnect();
        }
    };

    // 创建一个观察器实例并传入回调函数
    const observer = new MutationObserver(callback);
    // 以上述配置开始观察目标节点
    observer.observe(listenDom, config);
}

在App.vue的声明周期onBerforeMount运行

<script setup>
import { ref, onBeforeMount } from "vue";
// 查找次数,用于统计最大查找次数避免奔溃
const findAppCount = ref(0)

// 监听 dom变化
mutationAction(listenDom, callbackAction) {
    // 观察器的配置(需要观察什么变动)
    const config = { attributes: true, childList: true, subtree: true };
    // 当观察到变动时执行的回调函数
    const callback = function (mutationsList, observer) {
        // 渲染高度
        const renderHeight = listenDom.offsetHeight
        if (parseInt(renderHeight)) {
            // 第一次监听dom 存在高度则判定已经渲染完root节点
            callbackAction()
            // 停止观察
            observer.disconnect();
        }
    };

    // 创建一个观察器实例并传入回调函数
    const observer = new MutationObserver(callback);
    // 以上述配置开始观察目标节点
    observer.observe(listenDom, config);
}


const findAppDom = () => {
  const appDom = document.getElementById('app')
  findAppCount.value += 1
  if (appDom) {
    mutationAction(appDom, () => {
      const fmp=performance.now()
    })
  }
  else if (findAppCount <= 1000) {
    findAppDom()
  }
}
onBeforeMount(() => {
  // 白屏时间
  const fp = performance.now()
  findAppDom();
})
</script>

💖 使用条形图展示 fmp、fp时间

使用条形图对性能指标耗时进行可视化
line-bar
条形图展示数据的vue界面编写

<script lang="js" setup>
import { reactive, onMounted } from 'vue';
import * as echarts from 'echarts';

import { useStore } from "vuex";

const store = useStore();
const state = reactive({
    leftTitle: '原生的performance',
    leftDomId: 'visual-performance-id',

    rightTitle: '性能指标可视化',
    rightDomId: 'visual-performance-id-right',
    chartTitle: '性能指标',
    echartInstance: null,

})


const initLeftChart = () => {
    // 基于准备好的dom,初始化echarts实例
    const domInstance = document.getElementById(state.leftDomId)
    if (domInstance) {
        domInstance.removeAttribute('_echarts_instance_')
    }
    else {
        return
    }
    console.log(performance)
    console.log(Object.keys(performance.timing))
    const label = []
    const data = []
    for (let key in performance.timing) {
        if (key != 'toJSON') {
            label.push(key)
            data.push(performance.timing[key])
        }

    }
    const myChart = echarts.init(domInstance);
    const option = {
        title: {
            text: 'performance'
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'shadow'
            }
        },
        legend: {},
        grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true
        },
        xAxis: {
            type: 'value',
            boundaryGap: [0, 0.01]
        },
        yAxis: {
            type: 'category',
            data: label
        },
        series: [
            {
                name: 'performance',
                type: 'bar',
                data: data
            }
        ]
    };
    console.log('option', option)
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option, true);
    // 监听
    state.echartInstance = myChart;
    myChart.on('click', function (params) {
        console.log('params', params)
    });
    window.onresize = myChart.resize;
}

const initRightChart = () => {
    // 基于准备好的dom,初始化echarts实例
    const domInstance = document.getElementById(state.rightDomId)
    if (domInstance) {
        domInstance.removeAttribute('_echarts_instance_')
    }
    else {
        return
    }

    const performanceConfig = store.getters["common/performanceConfig"]

    console.log('performanceConfig________________', performanceConfig)

    const label = []
    const data = []

    Object.keys(performanceConfig).forEach(key => {
        data.push(performanceConfig[key])
        label.push(key)
    })
    const myChart = echarts.init(domInstance);
    const option = {
        title: {
            text: '自定义计算fmp'
        },
        tooltip: {
            trigger: 'axis',
            axisPointer: {
                type: 'shadow'
            }
        },
        legend: {},
        grid: {
            left: '3%',
            right: '4%',
            bottom: '3%',
            containLabel: true
        },
        xAxis: {
            type: 'value',
            boundaryGap: [0, 0.01]
        },
        yAxis: {
            type: 'category',
            data: label
        },
        series: [
            {
                name: 'fmp计算',
                type: 'bar',
                data: data
            }
        ]
    };
    console.log('option', option)
    // 使用刚指定的配置项和数据显示图表。
    myChart.setOption(option, true);
    // 监听
    state.echartInstance = myChart;
    myChart.on('click', function (params) {
        console.log('params', params)
    });
    window.onresize = myChart.resize;
}


onMounted(() => {
    initLeftChart()
    initRightChart()
})
</script>
<template>
    <div>

        <div style="display:flex;">
            <a-card :title="state.leftTitle" style="width: 600px">
                <div :id="state.leftDomId" style="width: 500px;height:600px;">

                </div>

            </a-card>

            <a-card :title="state.rightTitle" style="width: 600px;margin-left:20px">
                <div :id="state.rightDomId" style="width: 500px;height:600px;">

                </div>
                <div>

                </div>

            </a-card>
            <a-card style="margin-left:20px" title="定义">
                <div>
                    <div>
                        fetchStart:浏览器发起资源请求时,有缓存时,则返回读取缓存的开始时间。<br>

                        domainLookupStart:查询 DNS 的开始时间。<br>

                        domainLookupEnd:查询 DNS 的结束时间。<br>

                        connectStart:浏览器开始与服务器连接时的时间。<br>

                        secureConnectionStart:如果页面使用 HTTPS,它的值是安全连接握手之前的时刻。<br>

                        connectEnd:当浏览器端完成与服务器端建立连接的时刻。<br>

                        responseStart:指客户端收到从服务器端(或缓存、本地资源)响应回的第一个字节的数据的时刻。<br>

                        responseEnd:指客户端收到从服务器端(或缓存、本地资源)响应回的最后一个字节的数据的时刻。<br>

                    </div>
                </div>
            </a-card>
        </div>

    </div>
</template>

效果:
visual-fmp

⭐项目代码

前端项目inscode如下:

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!
earth

👍 点赞,是我创作的动力!
⭐️ 收藏,是我努力的方向!
✏️ 评论,是我进步的财富!
💖 最后,感谢你的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/444917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读:Diffusion Model-Based Image Editing: A Survey

Diffusion Model-Based Image Editing: A Survey 论文链接 GitHub仓库 摘要 这篇文章是一篇基于扩散模型&#xff08;Diffusion Model&#xff09;的图片编辑&#xff08;image editing&#xff09;方法综述。作者从多个方面对当前的方法进行分类和分析&#xff0c;包括学习…

图像处理与图像分析—图像的读入(C语言)

学习将会依据教材图像处理与图像分析基础&#xff08;C/C&#xff09;版内容展开 什么是数字图像处理 一副图像可以定义为一个二维函数 f(x&#xff0c;y) &#xff0c;其中 x 和 y 是空间&#xff08;平面&#xff09;坐标&#xff0c;任意一对空间坐标 (x,y) 处的幅度值 &am…

了解 HTTPS 中间人攻击:保护你的网络安全

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

二叉树进阶--二叉搜索树的进一步优化--AVL树 Self-balancing binary search tree

前言&#xff1a; 在上一次的文章中&#xff0c;我们详细介绍了二叉树的进阶树型&#xff0c;即BS树(二叉搜索树),但在文章的结尾&#xff0c;二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表…

golang实现正向代理和反向代理

文章目录 正向代理反向代理区别与联系:总结代理服务器实现正向代理反向代理正向代理 正向代理是客户端代理,它位于客户端和目标服务器之间。它的作用是保护客户端的隐私和安全。 如我们现在想要访问谷歌,但是由于某些原因,无法直接访问到谷歌,我们可以通过连接一台代理服务…

Redis缓存过期策略

文章目录 一、面试题二、redis内存1. Redis的内存大小怎么查看&#xff1f;2. 设置redis内存3. redis内存的OOM 三、redis内存淘汰策略1. redis的过期键删除策略2. redis缓存淘汰策略 一、面试题 1. 生产上你们redis内存设置多少&#xff1f; 2. 如何配置、修改redis内存大小…

YOLOV5 初体验:简单猫和老鼠数据集模型训练

1、前言 前两天&#xff0c;通过OpenCV 对猫和老鼠视频的抽取&#xff0c;提取了48张图片。这里不再介绍&#xff0c;可以参考之前的文章&#xff1a;利用OpenCV 抽取视频的图片&#xff0c;并制作目标检测数据集-CSDN博客 数据的目录如下&#xff1a; 项目的下载见文末 2、制…

基于Java的在线课程教学系统(Vue.js+SpringBoot)

目录 一、摘要1.1 系统介绍1.2 项目录屏 二、研究内容2.1 课程类型管理模块2.2 课程管理模块2.3 课时管理模块2.4 课程交互模块2.5 系统基础模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示4.1 管理后台4.2 用户网页 五、样例代码5.1 新增课程类型5.2 网站登录5.3 课…

第十一篇 - 应用于市场营销视频场景中的人工智能和机器学习技术 – Video --- 我为什么要翻译介绍美国人工智能科技巨头IAB公司(1)

IAB平台&#xff0c;使命和功能 IAB成立于1996年&#xff0c;总部位于纽约市。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司&#xff0c;互动广告局&#xff08;IAB- the Interactive Advertising Bureau&#xff09;自1996年成立以来&#xff0c;先后为700多家媒体…

为什么选择 Flink 做实时处理

优质博文&#xff1a;IT-BLOG-CN 为什么选择 Flink 【1】流数据更真实地反映了我们的生活方式&#xff08;实时聊天&#xff09;&#xff1b; 【2】传统的数据架构是基于有限数据集的&#xff08;Spark 是基于微批次数据处理&#xff09;&#xff1b; 【3】我们的目标&#xf…

ROS——ROS环境搭建

Ubuntu 安装完毕后&#xff0c;就可以安装 ROS 操作系统了&#xff0c;大致步骤如下: 配置ubuntu的软件和更新&#xff1b; 设置安装源&#xff1b; 设置key&#xff1b; 安装&#xff1b; 配置环境变量。 1.配置ubuntu的软件和更新 配置ubuntu的软件和更新&#xff0c;…

系统编程--makefile项目管理

这里写目录标题 介绍语法结构总览基础规则简介最简单的makefile对于基础规则的理解和应用总结 makefile时尽量使用更独立的命令&#xff0c;减少文件之间的耦合度需求以及解决总结 补充&#xff08;关于makefile中脚本命令的编写顺序&#xff09; 一级目录二级目录二级目录二级…

数据科学中的Python:NumPy和Pandas入门指南【第121篇—NumPy和Pandas】

数据科学中的Python&#xff1a;NumPy和Pandas入门指南 数据科学是当今数字时代中的一个重要领域&#xff0c;而Python是数据科学家们最喜爱的编程语言之一。在这篇博客中&#xff0c;我们将介绍Python中两个强大的库——NumPy和Pandas&#xff0c;它们在数据处理和分析中发挥…

java算法第十八天 | ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

110.平衡二叉树 leetcode链接 思路&#xff1a; 使用后序遍历分别求左右子树的高度&#xff0c;若高度只差大于一&#xff0c;则返回-1&#xff0c;否则返回当前节点的最大高度。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* Tree…

爬虫(五)

1. 前端JS相关 三元运算 v1 条件 ? 值A : 值B; # 如果条件成立v1值A&#xff0c;不成立v1等于值Bres 1 1 ? 99 : 88 # res99特殊的逻辑运算 v1 11 || 22 # Ture v2 9 || 14 # 9 v3 0 || 15 # 15 v3 0 || 15 || "zhangfei" # 15赋值和…

x86 Ubuntu上编译eudev给龙芯loongarch64架构主机使用

1、下载eudev库eudev-master.zip&#xff0c;链接&#xff1a;eudev库官方地址 2、下载龙芯的交叉编译工具&#xff1a;loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.2.tar.xz&#xff0c;链接&#xff1a;龙芯交叉编译官方地址 3、交叉编译器环境搭建 (1)、…

latex绘图中\begin{figure}[htbp]中的htbp什么意思

在LaTeX中&#xff0c;\begin{figure}[htbp] 用来开始一个图形环境&#xff0c;其中 [htbp] 是一个位置参数&#xff0c;用来指导LaTeX如何放置这个图形。 具体来说&#xff0c;[htbp] 中的每个字母代表一个放置选项&#xff1a; h&#xff1a;代表“here”&#xff0c;意味着…

【LeetCode: 299. 猜数字游戏 - 模拟 + 计数】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

springcloud第3季 consul服务发现注册,配置中心2

一 consul的作用 1.1 为何使用注册中心 为何要用注册中心&#xff1f; 1.A服务调用B服务&#xff0c;使用ip和端口&#xff0c;如果B服务的ip或者端口发生变化&#xff0c;服务A需要进行改动&#xff1b; 2.如果在分布式集群中&#xff0c;部署多个服务B&#xff0c;多个服…

Linux(Ubuntu)中安装vscode

①首先去vscode的官网下载.deb文件 网址&#xff1a;https://code.visualstudio.com/docs/?dvlinuxarm64_deb 注&#xff1a;如果linux端无法打开网页下载文件&#xff0c;可以在Windows端下载好用WinSCP传输到Linux。下载前注意下你的系统架构是arm还是amd&#xff0c;系统…
最新文章