以编程方式运行 Lighthouse 简介

已发表: 2022-03-10
快速总结 ↬能够以编程方式运行 Google 的 Lighthouse 分析套件提供了很多优势,尤其是对于更大或更复杂的 Web 应用程序。 以编程方式使用 Lighthouse 允许工程师为需要更多自定义的站点设置质量监控,而不是 Lighthouse 的直接应用程序(例如 Lighthouse CI)所允许的。 本文简要介绍了 Lighthouse,讨论了以编程方式运行它的优势,并介绍了基本配置。

Lighthouse 是 Google 的一套网站质量分析工具。 它允许您评估网站的性能、可访问性、SEO 等。 它也是高度可配置的,使其足够灵活以适用于所有站点,从最简单到高度复杂。 这种灵活性包括几种不同的测试运行方式,允许您选择最适合您的站点或应用程序的方法。

运行 Lighthouse 的最简单方法之一是通过 Chrome 的 DevTools Lighthouse 面板。 如果您在 Chrome 中打开您的网站,然后打开 Chrome 的 DevTools,您应该会看到一个“Lighthouse”选项卡。 从那里,如果您单击“生成报告”,您应该会获得有关您网站质量指标的完整报告。

然而,我在这篇文章中关注的是光谱的另一端。 使用 JavaScript 以编程方式运行 Lighthouse 允许我们配置自定义运行、挑选和选择我们想要测试的功能、收集和分析结果以及指定我们的站点和应用程序独有的配置选项。

例如,也许您在一个可通过多个 URL 访问的站点上工作——每个 URL 都有自己的数据和样式,甚至可能还有您希望能够分析的标记。 或者,也许您想从每个测试运行中收集数据并以不同的方式编译或分析它。 能够根据最适合您的站点或应用程序的方式来选择您希望如何运行 Lighthouse 分析,这样可以更轻松地监控站点质量并在问题堆积或给您造成太多问题之前查明您的站点存在的问题网站的用户。

以编程方式运行 Lighthouse 并不是每个站点的最佳选择,我鼓励您探索 Lighthouse 团队为使用该工具而构建的所有不同方法。 但是,如果您决定以编程方式使用 Lighthouse,那么下面的信息和教程应该可以帮助您入门。

自定义灯塔选项

以编程方式运行 Lighthouse 的优势不仅在于能够配置 Lighthouse 本身,还在于您可能想要或需要围绕 Lighthouse 测试做的所有事情。 Lighthouse 有一些很棒的文档可以帮助您入门。 然而,为了充分利用以编程方式运行它,您需要深入了解 Lighthouse 如何工作的两个主要方面:配置测试运行和报告测试结果。

Lighthouse 测试运行配置

配置 Lighthouse 测试运行是您喜欢的简单或复杂的任务之一。

以编程方式运行 Lighthouse 时,您可以在三个位置提供自定义选项:您将测试的 URL、Chrome 选项和 Lighthouse 配置对象。 您可以从 Lighthouse 文档中看到这三个都是运行 Lighthouse 的函数中的参数:

 function launchChromeAndRunLighthouse(url, opts, config = null) { return chromeLauncher.launch({chromeFlags: opts.chromeFlags}).then(chrome => { opts.port = chrome.port; return lighthouse(url, opts, config).then(results => { return chrome.kill().then(() => results.lhr) }); }); }

您可以使用所需的任何代码来创建这些参数。 例如,假设您有一个包含多个要测试的页面或 URL 的站点。 也许您想在 CI 环境中运行该测试作为 CI 任务的一部分,每次任务运行时检查所有必要的页面。 使用此设置,您可以使用 JavaScript 创建 URL 并创建一个循环,该循环将为每个 URL 运行 Lighthouse。

您可能需要的任何 Chrome 选项都可以在传递给 chrome-launcher 的对象中指定。 在文档中的示例中, opts对象包含一个我们称为chromeFlags的数组,您可以将其传递给 chrome-launcher 和一个端口,您可以在其中保存 chrome-launcher 正在使用的端口,然后将其传递给 Lighthouse。

最后,Lighthouse 配置对象允许您添加任何特定于 Lighthouse 的选项。 Lighthouse 包提供了一个默认配置对象,可以按原样使用或扩展和修改。 您可以使用此对象执行多种操作,包括指定要测试的 Lighthouse 测试类别。

跳跃后更多! 继续往下看↓

您可以使用emulatedFormFactor来指定是否希望测试在移动或桌面模拟器中运行。 您可以使用extraHeaders添加您可能需要在浏览器中使用的任何 cookie。 例如,在将结果输出为 HTML 的桌面模拟器上仅运行可访问性类别的测试可能具有如下所示的配置对象:

 const lighthouseOptions = { extends: 'lighthouse:default', settings: { onlyCategories: ['accessibility'], emulatedFormFactor:'desktop', output: ['html'], }, }

此示例表示最小配置。 你可以做的还有很多。 Lighthouse 配置文档有更多信息。 它们还有一组示例配置对象,用于一些更复杂的实现。

自定义结果报告

以编程方式运行 Lighthouse 时,您可以以三种格式选项中的一种或多种形式返回结果,并且 - 这是我认为最令人兴奋的部分 - 您可以访问原始 Lighthouse Result (LHR) 对象。

HTML、JSON、CSV

Lighthouse 将自动以三种不同的方式格式化结果:HTML、JSON 或 CSV。 这些都是基于基本 Lighthouse 报告模板的预配置结果,例如,如果您在 Chrome DevTools 中运行 Lighthouse 测试,您会看到这些结果。 在上一节的lighthouseOptions配置对象中,您可以看到一个用于output的键,其中包含一个带有单个字符串的数组: html 。 这指定我只希望返回格式为 HTML 的结果。 如果我想要 HTML 和 JSON 格式的结果,则该数组看起来像['html', 'json']

一旦 Lighthouse 运行,它将返回一个包含两个键的结果对象: reportlhr 。 我们将在下一节讨论lhr键的内容,但report键包含一个数组,其中的结果按照您的要求格式化。 因此,例如,如果我们请求了['html', 'json'] ,那么results.report[0]将包含我们格式化为 HTML 的结果,而results.report[1]将包含我们格式化为 JSON 的结果。

LHR 对象

以编程方式运行 Lighthouse 还可以让您访问更灵活的结果对象:LHR 对象。 该对象包含 Lighthouse 运行的原始结果和一些元数据。 更完整的文档可以在 Lighthouse Github 存储库中找到。

您可以通过多种方式使用这些结果,包括创建自定义报告和从多次运行中收集数据进行分析。

示例:针对移动设备和桌面设备运行可访问性测试

假设我有一个站点,它根据用户使用的是小屏幕还是大屏幕来加载不同的组件,这意味着每个版本的站点的 HTML 都会略有不同。 我想确保站点的两个版本在 Lighthouse 可访问性测试中都获得 95 分,并且没有代码被提交到不符合该标准的main分支。

注意如果您想查看下面分析 Sparkbox 主页的代码的工作示例,您可以在此处找到存储库。

我可以将 Lighthouse 配置为运行可访问性类别两次,为每一个提供不同的配置对象——一次将emulatedFormFactor设置为desktop ,另一次将其设置为mobile 。 一个简单的方法是创建一个包含两个对象的数组,如下所示。

 const lighthouseOptionsArray = [ { extends: 'lighthouse:default', settings: { onlyCategories: ['accessibility'], emulatedFormFactor:'desktop', output: ['html', 'json'], }, }, { extends: 'lighthouse:default', settings: { onlyCategories: ['accessibility'], emulatedFormFactor:'mobile', output: ['html', 'json'], }, }, ]

然后,我可以创建一个函数,该函数将遍历这个数组,并为在数组中找到的每个对象运行 Lighthouse 测试。

需要注意的一点是,每次运行之间必须引入延迟,否则 Chromium 可能会混淆并且运行会出错。 为了做到这一点,我添加了一个wait函数,它在setTimeout间隔完成时返回一个承诺。

 function wait(val) { return new Promise(resolve => setTimeout(resolve, val)); } function launchLighthouse(optionSet, opts, results) { return chromeLauncher .launch({ chromeFlags: opts.chromeFlags }) .then(async chrome => { opts.port = chrome.port; try { results = await lighthouse(url, opts, optionSet); } catch (e) { console.error("lighthouse", e); } if (results) reportResults(results, runEnvironment, optionSet, chrome); await wait(500); chrome.kill(); }); } async function runLighthouseAnalysis() { let results; const opts = { chromeFlags: ["--no-sandbox", "--headless"] }; for (const optionSet of lighthouseOptionsArray) { console.log("****** Starting Lighthouse analysis ******"); await launchLighthouse(optionSet, opts, results); } }

在这种情况下,我将结果发送到reportResults函数。 从那里,我将结果保存到本地文件,将结果打印到控制台,并将结果发送到一个函数,该函数将确定测试是否通过了我们的可访问性阈值。

 async function reportResults(results, runEnvironment, optionSet, chrome) { if (results.lhr.runtimeError) { return console.error(results.lhr.runtimeError.message); } await writeLocalFile(results, runEnvironment, optionSet); printResultsToTerminal(results.lhr, optionSet); return passOrFailA11y(results.lhr, optionSet, chrome); }

对于这个项目,我希望能够将 JSON 结果保存在我们的 CI 测试运行的指定目录中,并将 HTML 文件保存在我们的本地测试运行的指定目录中。 Lighthouse 返回这些不同类型结果的方式是按照请求的顺序排列在一个数组中。

因此,在本例中,在我们的lighthouseOptions对象中,我们的数组首先请求 HTML,然后是 JSON。 因此, report数组将首先包含 HTML 格式的结果,然后是 JSON 格式的结果。 然后writeToLocalFile函数将结果的正确版本保存在具有自定义名称的文件中。

 function createFileName(optionSet, fileType) { const { emulatedFormFactor } = optionSet.settings; const currentTime = new Date().toISOString().slice(0, 16); const fileExtension = fileType === 'json' ? 'json' : 'html'; return `${currentTime}-${emulatedFormFactor}.${fileExtension}`; } function writeLocalFile(results, runEnvironment, optionSet) { if (results.report) { const fileType = runEnvironment === 'ci' ? 'json' : 'html'; const fileName = createFileName(optionSet, fileType); fs.mkdirSync('reports/accessibility/', { recursive: true }, error => { if (error) console.error('error creating directory', error); }); const printResults = fileType === 'json' ? results.report[1] : results.report[0]; return write(printResults, fileType, `reports/accessibility/${fileName}`).catch(error => console.error(error)); } return null; }

我还想在测试运行完成时将结果打印到终端。 这提供了一种快速简便的方法来查看结果,而无需打开文件。

 function printResultsToTerminal(results, optionSet) { const title = results.categories.accessibility.title; const score = results.categories.accessibility.score * 100; console.log('\n********************************\n'); console.log(`Options: ${optionSet.settings.emulatedFormFactor}\n`); console.log(`${title}: ${score}`); console.log('\n********************************'); }

最后,如果可访问性分数不符合我的阈值分数 95,我希望能够通过测试运行。

 function passOrFailA11y(results, optionSet, chrome) { const targetA11yScore = 95; const { windowSize } = optionSet; const accessibilityScore = results.categories.accessibility.score * 100; if (accessibilityScore) { if (windowSize === 'desktop') { if (accessibilityScore < targetA11yScore) { console.error(`Target accessibility score: ${targetA11yScore}, current accessibility score ${accessibilityScore}`); chrome.kill(); process.exitCode = 1; } } if (windowSize === 'mobile') { if (accessibilityScore < targetA11yScore) { console.error(`Target accessibility score: ${targetA11yScore}, current accessibility score ${accessibilityScore}`); chrome.kill(); process.exitCode = 1; } } } }

我邀请大家一起玩,并探索 Lighthouse 可以帮助监控您的网站质量的所有不同方式。

最后的笔记

虽然我故意让上面的示例相对简单,但我希望它能让您很好地了解以编程方式运行 Lighthouse 时可以完成的工作。 我希望它能激发您找到使用这个灵活、强大工具的新方法。

正如彼得·德鲁克所说:

“如果你不能衡量它,你就不能改进它。”

不仅能够衡量而且监控我们的网站质量,尤其是对于复杂网站,将大大有助于我们建立更好的网络。

关于 SmashingMag 的进一步阅读

  • 移动优先体验的 A/B 测试
  • 如何测试设计概念的有效性
  • 手动可访问性测试的重要性
  • 使用 Tensorflow.js 为前端开发人员提供机器学习
  • 使用这些技巧开始 UI 设计以加快您的工作流程

当然,在 Smashing Workshops 中,Smashing Cat 探索新的见解。

有用的前端和用户体验位,每周交付一次。

借助工具帮助您更好地完成工作。 通过电子邮件订阅并获取 Vitaly 的智能界面设计清单 PDF

在前端和用户体验上。 受到 190.000 人的信赖。