前端性能精进之优化方法论(二)——分析 (上)

简介: 前端性能精进之优化方法论(二)——分析 (上)

 在上一节中曾提到过两种性能监控:SYN 和 RUM,那么对应的也有两种分析:数据分析和实验室分析。

  数据分析会通过采集上来的性能信息来剖析和定位可能存在的各种问题。

  实验室分析会通过某个线上或本地的测试工具对页面进行单点测试,得出性能分析报告。

  本文会对前者介绍一些分析实践,后者会介绍一些比较有名的性能测试工具。

  数据分析的前端代码已上传至 shin-admin,后端代码上传至 shin-server


一、数据分析


  在将数据采集到后就需要立刻存储,并且按百分位数计算后,需要定期计算和清理。

  各类图表的辅助可以更好的定位到发生的性能问题。

1)存储

  在将性能数据采集到后,就需要将它们存储到数据库中,例如 MySQL、MongoDB 等。

  2023-04-13 如果将数据存储在 MongoDB 中,那么就可以将 JSON 中的属性作为条件来查询,例如查询 measure 对象中的 TTFB 大于 10 的记录。

  而 MySQL 中的 JSON 都是字符串序列化后再存储到数据库中的,所以如果要对某个属性做查询,会比较困难。

  为了避免拖垮服务器,在服务端接收时会通过队列来异步新增。

  以我当前公司的实践为例,将性能数据存储到 web_performance 表中,表结构如下。

CREATE TABLE `web_performance` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `load` int(11) NOT NULL DEFAULT '0' COMMENT '页面加载总时间',
  `ready` int(11) NOT NULL DEFAULT '0' COMMENT '用户可操作时间',
  `paint` int(11) NOT NULL DEFAULT '0' COMMENT '白屏时间',
  `screen` int(11) NOT NULL DEFAULT '0' COMMENT '首屏时间',
  `measure` varchar(1000) COLLATE utf8mb4_bin NOT NULL COMMENT '其它测量参数,用JSON格式保存',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `day` int(11) NOT NULL COMMENT '格式化的天(冗余字段),用于排序,20210322',
  `hour` tinyint(2) NOT NULL COMMENT '格式化的小时(冗余字段),用于分组,11',
  `minute` tinyint(2) DEFAULT NULL COMMENT '格式化的分钟(冗余字段),用于分组,20',
  `identity` varchar(30) COLLATE utf8mb4_bin NOT NULL COMMENT '身份',
  `project` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '项目关键字,关联 web_performance_project 表中的key',
  `ua` varchar(600) COLLATE utf8mb4_bin NOT NULL COMMENT '代理信息',
  `referer` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '来源地址',
  `referer_path` varchar(45) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '来源地址中的路径',
  `timing` text COLLATE utf8mb4_bin COMMENT '浏览器读取到的性能参数,用于排查',
  `resource` text COLLATE utf8mb4_bin COMMENT '静态资源信息',
  PRIMARY KEY (`id`),
  KEY `idx_project_day` (`project`,`day`),
  KEY `idx_project_day_hour` (`project`,`day`,`hour`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能监控'

  referer_path 字段,用于分析指定页面的性能。

  表中的 project 字段会关联 web_performance_project 表(结构如下所示)中的 key。

CREATE TABLE `web_performance_project` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '唯一值',
  `name` varchar(45) COLLATE utf8mb4_bin NOT NULL COMMENT '项目名称',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常  0:删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能监控项目';

  性能项目就是要监控的页面,与之前不同,性能的监控粒度会更细,因此需要有个后台专门管理这类数据。

  key 值是通过名称得到 16 位 MD5 字符串,需要引入 Node.js 的 cryto 库,如下所示。

const crypto = require('crypto');
const key = crypto.createHash('md5').update(name).digest('hex').substring(0, 16);

  可以对长期维护的网页创建单独的性能项目,而对于那些临时活动可以共用一个项目。

2)百分位数

  均值会受极值的影响,从而让它不够准确,无法真实的反映出用户的性能情况。

  故而选择了百分位数来解决极值问题,例如前 95% 用户的首屏时间在 2s 内,

  这种写法也叫 TP95,表示 95 分位数,TP 是 Top Percentile 的缩写。

  95 分位数是比较高的统计指标,意味着大多数的用户都能享受到更好的性能体验。

  为了能看到变化趋势,可以采用图表的方式,例如折线图,如下所示,横坐标可按天或小时。

  

3)定时任务

  每天可以选一个时间(例如凌晨三点),来统计昨天的日志信息。

  例如将计算得到的统计信息以 JSON 格式(如下所示)存储到数据库表的一个字段中。

  这个字段可以是 TEXT 类型或更大的 MEDIUMTEXT,一天只插入一条记录。

{
  hour: {
    x: [11, 14],
    load: ["158", "162"],
    ready: ["157", "162"],
    paint: ["158", "162"],
    screen: ["157", "162"]
  },
  minute: {
    11: {
      x: [11, 18, 30],
      load: ["157", "159", "160"],
      ready: ["156", "159", "160"],
      paint: ["157", "159", "160"],
      screen: ["156", "159", "160"]
    },
    14: {
      x: [9, 16, 17, 18],
      load: ["161", "163", "164", "165"],
      ready: ["161", "163", "164", "165"],
      paint: ["161", "163", "164", "165"],
      screen: ["161", "163", "164", "165"]
    }
  }
}

  还可以选一个时间来做数据清理,因为没有必要一直将这么多的数据保留着。

4)资源瀑布图

  通过资源瀑布图可以查看当时的资源加载情况。

  在上报性能参数时,将静态资源的耗时通过 getEntriesByType() 方法得到(如下所示),然后一起打包给服务器。


// 静态资源列表
const resources = performance.getEntriesByType("resource");
const newResources: TypeSendResource[] = [];
resources && resources.forEach((value: PerformanceResourceTiming): void => {
    // 过滤 fetch 请求
    if (value.initiatorType === "fetch") return;
    // 只存储 1 分钟内的资源
    if (value.startTime > 60000) return;
    newResources.push({
      name: value.name,
      duration: rounded(value.duration),
      startTime: rounded(value.startTime)
    });
  });
obj.resource = newResources;

  由于我本地业务请求使用的是 XMLHTTPRequest,因此在代码中会过滤掉 fetch 请求,只在上报监控数据时采用了 fetch() 函数。

  这可以根据实际情况来处理。在搜集资源时,1 分钟以外的都会舍弃,并且只记录了资源名称、耗时和开始时间。

  最终的效果如下图所示,包含一个横向的柱状图,并且在图中会标注白屏、首屏、load 和 DOMContentLoaded 的时间点。

  

  这样能对资源的加载做更直观的比较,便于定位性能问题。

5)堆叠柱状图

  先将所有的性能记录统计出来,然后分别统计白屏和首屏 1 秒内的数量、1-2 秒内、2-3 秒内、3-4 秒内、4+秒的数量,白屏的 SQL 如下所示。

SELECT COUNT(*) FROM `web_performance` WHERE `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` <= 1000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 1000 and `paint` <= 2000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 2000 and `paint` <= 3000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 3000 and `paint` <= 4000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 4000 `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';

  算出后,分母为总数,分子为上述五个值,组成一张堆叠柱状图,类似于下面这样,每种颜色代码一个占比。

  

  这样就能直观的看到优化后的性能变化了,可更快的反馈优化结果。

6)阶段时序图

  在将统计的参数全部计算出来后,为了能更直观的发现性能瓶颈,设计了一张阶段时序图。

  描绘出 TTFB、responseDocumentTime、initDomTreeTime、parseDomTime 和 loadEventTime 所占用的时间,如下所示。

  橙色竖线表示白屏时间,黑色竖线表示首屏时间。移动到 id 或来源地址,就会提示各类参数。

  

相关文章
|
7天前
|
缓存 前端开发 JavaScript
利用代码分割优化前端性能:策略与实践
在现代Web开发中,代码分割是提升页面加载性能的有效手段。本文介绍代码分割的概念、重要性及其实现策略,包括动态导入、路由分割等方法,并探讨在React、Vue、Angular等前端框架中的具体应用。
|
12天前
|
编解码 前端开发 JavaScript
从入门到精通:揭秘前端开发中那些不为人知的优化秘籍!
前端开发是充满无限可能的领域,从初学者到资深专家,每个人都追求更快、更稳定、更用户体验友好的网页。本文介绍了四大优化秘籍:1. HTML的精简与语义化;2. CSS的优雅与高效;3. JavaScript的精简与异步加载;4. 图片与资源的优化。通过这些方法,可以显著提升网页性能和用户体验。
17 3
|
21天前
|
缓存 前端开发 JavaScript
前端性能优化:Webpack与Babel的进阶配置与优化策略
【10月更文挑战第28天】在现代Web开发中,Webpack和Babel是不可或缺的工具,分别负责模块打包和ES6+代码转换。本文探讨了它们的进阶配置与优化策略,包括Webpack的代码压缩、缓存优化和代码分割,以及Babel的按需引入polyfill和目标浏览器设置。通过这些优化,可以显著提升应用的加载速度和运行效率,从而改善用户体验。
39 6
|
23天前
|
缓存 监控 前端开发
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第26天】前端工程化是现代Web开发的重要趋势,通过将前端代码视为工程来管理,提高了开发效率和质量。本文详细对比了Webpack和Gulp两大主流构建工具的选择与配置优化,并提供了具体示例代码。Webpack擅长模块化打包和资源管理,而Gulp则在任务编写和自动化构建方面更具灵活性。两者各有优势,需根据项目需求进行选择和优化。
56 7
|
22天前
|
JavaScript 前端开发 开发者
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第27天】在前端开发领域,Vue.js和Angular是两个备受瞩目的框架。本文对比了两者的优劣,Vue.js以轻量级和易上手著称,适合快速开发小型到中型项目;Angular则由Google支持,功能全面,适合大型企业级应用。选择时需考虑项目需求、团队熟悉度和长期维护等因素。
32 1
|
22天前
|
缓存 前端开发 JavaScript
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第27天】在现代前端开发中,构建工具的选择对项目的效率和可维护性至关重要。本文比较了Webpack和Gulp两个流行的构建工具,介绍了它们的特点和适用场景,并提供了配置优化的最佳实践。Webpack适合大型模块化项目,Gulp则适用于快速自动化构建流程。通过合理的配置优化,可以显著提升构建效率和性能。
33 2
|
23天前
|
JavaScript 前端开发 API
前端框架对比:Vue.js与Angular的优劣分析与选择建议
【10月更文挑战第26天】前端技术的飞速发展让开发者在构建用户界面时有了更多选择。本文对比了Vue.js和Angular两大框架,介绍了它们的特点和优劣,并给出了在实际项目中如何选择的建议。Vue.js轻量级、易上手,适合小型项目;Angular结构化、功能强大,适合大型项目。
21 1
|
29天前
|
缓存 前端开发 JavaScript
前端性能优化:打造流畅用户体验的秘籍
【10月更文挑战第20天】前端性能优化:打造流畅用户体验的秘籍
34 3
|
28天前
|
存储 缓存 算法
前端算法:优化与实战技巧的深度探索
【10月更文挑战第21天】前端算法:优化与实战技巧的深度探索
21 1
|
28天前
|
缓存 前端开发 JavaScript
如何优化前端资源
如何优化前端资源

热门文章

最新文章

下一篇
无影云桌面