☰
从 6.0 说起:sitespeed.io 插件系统重构与核心架构演进
2026/10/13 6:48:10 网站建设 项目流程
  • 测试
  • 性能剖析
  • 指标监控

【免费下载链接】sitespeed.io

sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.

项目地址:https://gitcode.com/gh_mirrors/si/sitespeed.io
点击查看免费下载

导读

本文以 sitespeed.io 6.0 发布前的技术预告为切入点,系统梳理该版本围绕「插件系统重构」所做的核心工作:消息队列驱动的插件通信机制、sitespeedio.setup全新队列阶段、HTML 输出可插拔化、基于 filterRegistry 的指标过滤、Docker 默认开启视频与 SpeedIndex 等面向初学者的改进,以及与 Browsertime 2.0、The Coach 1.0、PageXray 2.0 的协同演进。读完本文,你将理解 sitespeed.io 的插件如何通过队列彼此对话、如何为插件生成 HTML 页面与自定义 CSS,并能依据当前仓库源码定位这些机制的实现位置,为开发自己的插件打下基础。


一、6.0 预告发布的技术背景

2017 年 11 月,sitespeed.io 团队在 5.0 发布半年后,于 docs/_posts/2017-11-02-sitespeed.io-6.0-coming-soon.md 发布了 6.0 的预告(当时仓库版本号现为 42.7.0,见 package.json,但插件机制的设计脉络自 6.0 起延续至今)。该文档明确列出了 6.0 的改进清单,核心围绕以下方向:

  • 使用 Chartist 展示视觉进度、页面大小与请求数,降低数据阅读门槛;
  • HTML 页面适配更大屏幕(更大分辨率下的响应式展示);
  • 升级为官方 Graphite Docker 容器并以 Graphite 1.X 为默认;
  • 在日志中输出结果的完整 URL,便于把日志与结果文件对应起来;
  • 重构插件体系:在上下文(context)中暴露 messageMaker 供插件发送队列消息、暴露 filterRegistry 供插件注册指标以便被 Graphite/InfluxDB 等采集,并把核心功能收敛到 core 目录;
  • Docker 运行默认开启--video与--speedIndex,降低初学者上手门槛;
  • 支持插件生成 HTML(按 run 或按 page summary);
  • 支持插件通过发送html.css消息覆盖/追加 CSS;
  • 文档全面重写;
  • The Coach 1.0 发布,针对 Google Analytics 与 Google Tag Manager 的建议做了调整。

随后的正式发布文档 docs/_posts/2017-11-27-sitespeed.io-6.0.md 确认:6.0 把 Node.js 从 6 升级到 8(并同步带动 Browsertime 2.0、PageXray 2.0、The Coach 1.0),核心聚焦于插件结构更新——让插件之间可以通过队列通信。

下文将逐一深入讲解这些改进在仓库源码中的实际形态。


二、消息队列:插件间通信的基石

2.1 一切皆消息

6.0 最根本的架构变化是让「插件彼此通过队列通信」。在 lib/core/queue.js 中,createQueue()实现了一个支持并发限制的队列:

  • queue.limit({ concurrency })设置并发度;
  • queue.process(fn)注册每条消息的处理函数;
  • queue.enqueued / processingStarted / processingEnded / drained提供生命周期回调;
  • queue.isDrained在任务清空时返回true。

在 lib/core/queueHandler.js 中,QueueHandler.createQueues()会为每个实现processMessage的插件创建一条独立队列,并为每条消息记录排队耗时与处理耗时(registerQueueTime/registerProcessingTime,实现于 lib/core/queueStatistics.js),供--queueStats输出统计。消息通过postMessage()广播给所有插件队列,每个插件在自己的队列中依次消费。

每条消息的结构由 lib/support/messageMaker.js 定义:

export function messageMaker(source) { return { make(type, data, extras) { const stamp = timestamp().format(); const uuid = randomUUID(); return merge({ uuid, type, timestamp: stamp, source, data }, extras); } }; }

即消息至少包含:

  • uuid:全局唯一标识;
  • type:消息类型,插件据此判断是否感兴趣(如browsertime.run、pagexray.pageSummary、html.pug);
  • timestamp:创建时间;
  • source:消息来源(产生消息的插件名);
  • data:携带的数据;
  • extras:额外字段(如url、group),可自由扩展。

2.2 运行阶段与sitespeedio.setup

lib/core/queueHandler.js 的run()方法是整个执行流程的骨架,注释清晰地定义了各个阶段:

setup - 插件之间互相通信、做各自所需的初始化 url - 传入待分析的 URL summarize - 全部分析完成后汇总数据 prepareToRender - 渲染前的准备 render - 插件把数据写入磁盘 final - 退出前的收尾(如上传到 S3)

对应的调用链为:

this.startProcessingQueues() .then(() => this.postMessage(make('sitespeedio.setup'))) .then(() => this.drainAllQueues()) .then(async () => { for (let source of sources) { await source.findUrls(this, this.options); } }) .then(() => this.drainAllQueues()) .then(() => this.postMessage(make('sitespeedio.summarize'))) .then(() => this.drainAllQueues()) .then(() => this.postMessage(make('sitespeedio.prepareToRender'))) .then(() => this.drainAllQueues()) .then(() => this.postMessage(make('sitespeedio.render'))) .then(() => this.drainAllQueues())

其中sitespeedio.setup是 6.0 引入的全新队列阶段:它是第一条广播给所有插件的消息,插件收到后即可告知其他插件自己已就绪、并向它们发送配置。例如:

  • lib/plugins/browsertime/index.js 在收到sitespeedio.setup时回复browsertime.setup,通知其他插件「Browsertime 已就绪,可以给我发送配置」;
  • lib/plugins/graphite/index.js 同样在sitespeedio.setup阶段回复graphite.setup;
  • 插件可以在该阶段向 HTML 插件发送html.pug(注册模板)、向 Browsertime 发送browsertime.scripts(注入 JavaScript),详见下文。

此外,lib/sitespeed.js 中构造的context对象把messageMaker、filterRegistry、statsHelpers、storageManager、resultUrls、getLogger等统一暴露给插件——这正是 6.0 预告中「把 messageMaker 与 filterRegistry 暴露到 context」的具体落地。插件加载逻辑在 lib/core/pluginLoader.js:默认插件集、--plugins.add/--plugins.remove/--plugins.list的增删查都在此处理,并且支持从内置目录、相对路径、npm 包名、全局安装包四种方式加载插件。

提示:messageMaker与queueHandler的完整调用链,可以结合 lib/sitespeed.js 阅读——它是整个运行流程的入口,先解析插件名、加载插件、调用每个插件的open(context, options),再进入queueHandler.run(urlSources),最后调用close(options, errors)。


三、让插件生成 HTML:html.pug 与 html.css

3.1 插件如何注册 Pug 模板

6.0 的另一大能力是「插件可以生成 HTML」。机制是:插件在sitespeedio.setup阶段向 HTML 插件发送html.pug消息,HTML 插件随后会把该插件的指标数据渲染成独立的 Tab。

html.pug消息需要携带四个字段:

字段说明
id插件唯一标识,用于拼装消息类型(如gpsi→gpsi.pageSummary)
name在 Tab 上显示的名称(友好名)
pugPug 模板内容(字符串)
typerun/pageSummary/summary:数据是按每次运行、每页汇总还是全局汇总展示

发送示例(对应 docs/documentation/sitespeed.io/plugins/index.md 中的官方示例):

case 'sitespeedio.setup': { queue.postMessage( make('html.pug', { id: 'gpsi', name: 'GPSI', pug: this.pug, type: 'pageSummary' }) ); break; }

HTML 插件收到html.pug后的处理在 lib/plugins/html/index.js 中:

case 'html.pug': { // we got a pug from plugins, let compile and cache them addTemplate(message.data.id, message.data.pug); // and also keep the types so we can render them this.HTMLBuilder.addType( message.data.id, message.data.name, message.data.type ); // make sure we pickup the data for this pug this.collectDataFrom.push(message.data.id + '.' + message.data.type); break; }

addTemplate在 lib/plugins/html/renderer.js 中实现——用pug包的compile()把模板字符串编译并缓存,模板文件名就是id + '.pug'。同时HTMLBuilder.addType()(见 lib/plugins/html/htmlBuilder.js)根据type把模板挂到 run 页、pageSummary 页或 summary 页的 Tab 列表,并把id.type追加进collectDataFrom——HTML 插件会自动收集所有形如*.run、*.pageSummary的消息数据,按 URL 归位(dataCollector.addDataForUrl)。

插件发送数据时,只需用id + type拼出消息类型并带上 URL:

queue.postMessage( make('gpsi.pageSummary', result, { url, group }) );

3.2 插件如何追加 CSS

如果插件希望覆盖或追加 HTML 报告样式,只需发送类型为html.css的消息。HTML 插件在 lib/plugins/html/index.js 中通过HTMLBuilder.addInlineCSS(message.data)接收,并在渲染时把累积的 CSS 以内联样式合入最终页面(见 lib/plugins/html/htmlBuilder.js 中this.inlineCSS.join(''))。

这一机制让第三方插件无需修改 HTML 插件源码即可定制报告的视觉呈现,是 6.0 预告中「插件可覆盖/追加 CSS」的直接实现。

3.3 默认注册的数据类型

HTML 插件在open()中注册了一组默认会收集的数据类型(见 lib/plugins/html/index.js):

this.collectDataFrom = [ 'browsertime.run', 'browsertime.console', 'browsertime.pageSummary', 'pagexray.run', 'pagexray.pageSummary', 'coach.run', 'coach.pageSummary', 'thirdparty.run', 'thirdparty.pageSummary', 'crux.pageSummary' ];

这些类型的消息会被自动收集、按 URL 归档,最终由sitespeedio.render消息触发的HTMLBuilder.render()渲染成 HTML 报告。插件新增类型只需在sitespeedio.setup阶段注册html.pug,其id.type会被自动追加到该列表。


四、让插件通过 Browsertime 采集指标

6.0 的另一项重要能力是「插件可以在被测页面上运行 JavaScript 采集指标」。Browsertime 插件在 lib/plugins/browsertime/index.js 中监听两类消息:

  • browsertime.scripts:同步脚本,插件需提供category与scripts(脚本名到脚本体的映射);
  • browsertime.asyncscripts:异步脚本,结构相同。

插件在sitespeedio.setup阶段发送脚本:

case 'sitespeedio.setup': { queue.postMessage( make('browsertime.scripts', { category: 'yourplugin', scripts: { userAgent: '(function() {return navigator.userAgent;})();', title: '(function() {return document.title;})();' } }) ); break; }

Browsertime 在页面加载时执行这些脚本,并把结果合并进browsertime.run消息的数据中;插件监听browsertime.run即可取回:

case 'browsertime.run': { console.log(message.data.yourplugin); break; }

在 Pug 模板中可通过pageInfo.data.browsertime.run.yourplugin.title访问。注意browsertime.scripts消息要求同时携带category与scripts字段,否则 Browsertime 插件会记录格式错误的日志(见 lib/plugins/browsertime/index.js 中对message.data.category && message.data.scripts的校验)。

从源码看,Browsertime 插件还会在sitespeedio.setup阶段广播browsertime.config(若开启截图,通知 HTML 插件截图类型)与browsertime.alias(URL 别名/分组),Graphite 插件也会监听browsertime.setup来决定是否采集browsertime.pageSummary的注解数据——这些都是一次sitespeedio.setup广播在多个插件间产生的联动效应。


五、filterRegistry:决定哪些指标进入时序数据库

6.0 预告中提到「暴露 filterRegistry 到 context,让插件注册应被 Graphite/InfluxDB 等采集的指标」。该机制实现在 lib/support/filterRegistry.js:

  • registerFilterForType(filter, type):为某消息类型注册指标白名单;
  • addFilterForType(filter, type):追加过滤规则;
  • getFilterForType(type)/getFilters()/getTypes():查询已注册的过滤规则;
  • filterMessage(message):对消息按注册的过滤规则裁剪数据(调用 lib/support/metricsFilter.js 的filterMetrics)。

Browsertime 插件在open()中即为三类消息注册了默认过滤规则(见 lib/plugins/browsertime/index.js):

context.filterRegistry.registerFilterForType( DEFAULT_METRICS_PAGE_SUMMARY, 'browsertime.pageSummary' ); context.filterRegistry.registerFilterForType( DEFAULT_METRICS_SUMMARY, 'browsertime.summary' ); context.filterRegistry.registerFilterForType( this.options.limitedRunData ? DEFAULT_METRICS_RUN_LIMITED : DEFAULT_METRICS_RUN, 'browsertime.run' );

这些默认指标清单定义在 lib/plugins/browsertime/default/ 目录下(metricsPageSummary.js、metricsSummary.js、metricsRun.js、metricsRunLimited.js)。Graphite 插件在 lib/plugins/graphite/index.js 中持有context.filterRegistry,并通过--graphite.messages配置决定采集哪些消息类别;自定义插件只需在open()中调用context.filterRegistry.registerFilterForType(...),即可让自己的指标进入 Graphite/InfluxDB 等时序数据库的采集范围。


六、Docker 默认开启视频与 SpeedIndex

6.0 预告特别指出「Running Docker adds --video and --speedIndex by default to make it easier for beginners」。在 lib/cli/options/video.js 中可以看到这些参数当前的形态与别名:

  • --browsertime.visualMetrics(别名--visualMetrics、--speedIndex):计算 SpeedIndex、First Visual Change、Last Visual Change 等视觉指标,依赖 FFMpeg 与 Python;
  • --browsertime.video(别名--video):录制并保存视频;要完全关闭需同时设置video与visualMetrics为false;
  • --browsertime.videoParams.framerate(别名--fps,默认 30):帧率——这正是 Browsertime 2.0 中「默认帧率从 60 降到 30」的体现;
  • --browsertime.videoParams.convert(默认true):把原始视频转换为通用可播放格式;
  • --browsertime.videoParams.createFilmstrip(默认true)、--browsertime.videoParams.filmstripQuality(默认 75)、--browsertime.videoParams.filmstripFullSize(默认false):胶片条(filmstrip)截图开关、质量与全尺寸选项;
  • --browsertime.videoParams.addTimer(默认true):在视频上叠加计时器与指标。

当前的 Dockerfile 中设置了ENV SITESPEED_IO_BROWSERTIME__XVFB=true与SITESPEED_IO_BROWSERTIME__DOCKER=true,容器入口为 docker/scripts/start.sh,它负责启动 ADB(如需 Android 调试)、以正确的用户身份运行sitespeed.io,并支持REPLAY模式(WebPageReplay 录制/回放,配合 docker/webpagereplay/ 下的 wpr 二进制与证书)。这意味着在 Docker 内运行测试时,视频与视觉指标相关的环境(FFMpeg、Xvfb、Python)已经就绪,初学者无需手动配置。

相关视频参数在 Browsertime 2.0 发布文档中的说明(docs/_posts/2017-11-27-sitespeed.io-6.0.md):

  • 默认帧率改为 30(--videoParams.framerate),以适配小型云主机;
  • 视频录制分两步:先无损录制、再转换为通用格式,降低 ffmpeg 的 CPU 占用并使指标更稳定;
  • 可用--videoParams.combine把 preScript/postScript 的执行过程并入视频,方便调试脚本。

七、Graphite 1.X 与官方容器

6.0 还升级了默认的 Graphite 部署:改用官方 Graphite Docker 容器、以 Graphite 1.X 为默认。当前仓库的 docker/docker-compose.yml 使用sitespeedio/graphite:1.1.10-3镜像,暴露 2003(carbon 接收端口)与 8080(Graphite Web);Grafana 数据源通过 docker/grafana/provisioning/datasources/graphite.yaml 以 proxy 方式接入http://graphite:80,并默认匿名访问开启。

配套的 Graphite 配置(carbon 存储周期、聚合规则等)位于 docker/graphite/conf/ 下(carbon.conf、storage-schemas.conf、storage-aggregation.conf),docker-compose 注释中明确提示生产环境应将 whisper 数据与 graphite.db 挂载到容器外持久化。

Graphite 插件的采集逻辑在 lib/plugins/graphite/index.js:支持--graphite.host、--graphite.port、--graphite.namespace、--graphite.messages、--graphite.bulkSize、--graphite.sendAnnotation、--graphite.addSlugToKey等配置(缺少 host 时会直接抛错),并支持 StatsD 模式(isStatsD判定,对应 lib/plugins/graphite/helpers/is-statsd.js)。发送逻辑由 lib/plugins/graphite/graphite-sender.js 与 lib/plugins/graphite/statsd-sender.js 实现。


八、The Coach 1.0 与 PageXray 2.0

6.0 系列发布还同步推进了生态工具:

  • The Coach 1.0:Coach 是基于 HAR 的网页最佳实践建议引擎(coach-core)。1.0 版本针对 Google Analytics 与 Google Tag Manager 的建议做了调整,让作为「独立工具」的 Coach 对这两大第三方脚本给出更准确的评分。当前仓库中 Coach 数据由 lib/plugins/coach/ 收集,核心引擎来自coach-core依赖(见 package.json 的 dependencies)。
  • PageXray 2.0:以 bug 修复与升级 NodeJS 8 为主。PageXray 负责解析 HAR、生成按域名/内容类型的资源统计,其插件实现位于 lib/plugins/pagexray/,DOM 层面则由 lib/plugins/domains/ 处理按域名汇总。

注意:以上描述均以当前仓库的实际状态为准——仓库已演进到 42.7.0,6.0 时期的细节以发布文档与源码交叉印证为准,具体参数请以你正在使用的版本为准。


九、从 6.0 到现在的插件开发路径

6.0 奠定的消息队列架构延续至今。当前开发插件遵循 docs/documentation/sitespeed.io/plugins/index.md 的指南:插件继承@sitespeed.io/plugin提供的SitespeedioPlugin基类,实现constructor(options, context, queue)、open(context, options)、processMessage(message, queue)、close(options, errors),并通过super.sendMessage(...)/queue.postMessage(...)收发消息。

9.1 插件的生命周期

  • open():启动时调用一次,可获取context与options,做初始化(如注册 filterRegistry、准备存储);
  • processMessage():每条队列消息都会经过插件,插件按message.type决定是否处理;
  • close():全部 URL 分析完成后调用一次,用于收尾(如关闭资源)。

9.2 重要的内置消息

  • sitespeedio.setup:第一条消息,插件在此向其他插件广播自身能力/配置;
  • sitespeedio.summarize:所有 URL 分析完毕,插件需汇总指标(产出*.summary);
  • sitespeedio.render:渲染(写盘)时刻,HTML 插件在此渲染报告并广播html.finished;
  • *.run/*.pageSummary/*.summary:按运行、按页面汇总、按全部页面汇总的三级数据消息。

插件的加载机制(lib/core/pluginLoader.js)支持四种方式:

# 内置/本地目录 --plugins.add /sitespeed.io/myplugin # 相对路径 --plugins.add ../my/super/plugin # npm 包名(本地或全局安装) npm install @sitespeed.io/plugin-lighthouse -g --plugins.add @sitespeed.io/plugin-lighthouse # 预烘焙 Docker 镜像(Dockerfile 中设置 SITESPEED_IO_PLUGINS__ADD)

9.3 让插件指标进入性能预算

插件还可以把自己的指标交给 budget 插件做预算检查:在sitespeedio.setup阶段发送budget.addMessageType消息声明要采集的消息类型,例如:

queue.postMessage(make('budget.addMessageType', {type: 'gpsi.pagesummary'}));

十、验证你的理解:源码阅读路径

如果你想亲自验证上述机制,可按以下路径阅读当前仓库:

  1. lib/sitespeed.js —— 运行入口:解析插件、构建 context、编排整个队列流程;
  2. lib/core/queue.js 与 lib/core/queueHandler.js —— 队列实现与setup → url → summarize → prepareToRender → render阶段编排;
  3. lib/support/messageMaker.js 与 lib/support/filterRegistry.js —— 消息构造与指标过滤;
  4. lib/plugins/html/index.js —— 如何消费html.pug、html.css并收集*.run/*.pageSummary数据;
  5. lib/plugins/browsertime/index.js —— 如何消费browsertime.scripts/browsertime.asyncscripts、响应sitespeedio.setup;
  6. lib/plugins/graphite/index.js —— 如何利用 filterRegistry 与sitespeedio.setup完成指标落库;
  7. lib/core/pluginLoader.js —— 默认插件集与--plugins.add/remove/list的解析逻辑。

结语

sitespeed.io 6.0 预告文档所勾勒的改进——消息队列驱动的插件体系、可生成 HTML 的插件、可注入的 CSS、Docker 默认视频与 SpeedIndex、Graphite 官方容器、Coach 1.0——在近十年后的当前仓库源码中依然清晰可辨,并构成了整个插件生态的基石。理解sitespeedio.setup这一条消息如何串联起 Browsertime、HTML、Graphite 等插件之间的协作,你就掌握了为 sitespeed.io 编写可复用、可共享的第三方插件所需的核心心智模型。

  • 测试
  • 性能剖析
  • 指标监控

【免费下载链接】sitespeed.io

sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.

项目地址:https://gitcode.com/gh_mirrors/si/sitespeed.io
点击查看免费下载
上一篇:antd Select 三种尺寸详解:large / middle / small 的高度标准与实现原理
下一篇:Hugo 组件(Component)体系详解:七大组件类型与统一文件系统

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询