您的当前位置:首页正文

如何使用NodeJS+Lighthouse+Gulp搭建自动化网站性能测试的工具

2020-11-27 来源:品趣旅游知识分享网
这篇文章主要介绍了关于如何使用NodeJS + Lighthouse + Gulp搭建自动化网站性能测试的工具,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

假设你还不知道Lighthouse是什么

Lighthouse 是Google公司旗下一个开源的、可自动化检测网站质量的工具,界面友好、操作简单、使用方式多样、视角全面,可以用它来测试任意网页,普通用户、QA、开发都可以快速上手。

启动姿势

难度系数 +1

使用Lighthouse的方式有很多种,最简单的,可以使用 Chrome 的开发者工具,步骤如下:

  1. 打开 Chrome 浏览器

  2. 按F12

  3. 在弹出来的窗口中打开 audits 标签

  4. 点击 Perform an audit...勾选全部

  5. Run audit

难度系数+2

也可以使用命令行。

  1. 安装Node

  2. 安装Lighthouse npm install -g lighthouse

  3. 在命令行中run lighthouse <url>

以上两种使用方式都不是本文的重点,如果想深入了解,可参照 Run Lighthouse in DevTools

难度系数+3

由于最近在学习 NodeJS, 因此笔者决定使用 Node 8 + Gulp 来跑 lighthouse,为了提高结果的准确性,每次task都跑10次 lighthouse, 并且只关心结果指标中的 first-meaningful-paint 毫秒数,最终取10次的平均值,为了可视化与可读性,最终的结果以网页的形式展示,用户可在网页上看到每次执行 Lighthouse 之后 first-meaningful-paint 的毫秒数,也可以看到平均值,如果用户对某次执行的细节感兴趣,可以点击链接察看。最终的结果长这个样子:

355591978-5b484ee3e3c0f_articlex[1].png

4269315244-5b47271d5b48b_articlex[1].png

环境搭建

安装 Node 8

安装依赖包

npm i lighthouse --save-dev
npm i chrome-launcher --save-dev
npm i fs-extra --save-dev
npm i gulp --save-dev

配置

在项目根目录下创建Lighthouse的配置文件 lighthouse-config.js, 这里我们全部使用默认配置,使用默认配置需在配置文件中声明 extends: 'lighthouse:default'

module.exports = {
 extends: 'lighthouse:default'
}

如果读者需要了解更详细的配置选项,可参考:

  1. Lighthouse 这篇大部分内容是关于命令行的,命令行参数同样可用于Node

  2. throttling这篇是关于网络模拟的

  3. Default Config 具体的默认配置参数

  4. Web Page Test 模拟不同的网速

  5. Emulation 模拟不同的设备

Coding

在项目根目录下创建 gulpfile.js,首先引入所有依赖的工具:

const gulp = require('gulp');
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
const printer = require('lighthouse/lighthouse-cli/printer');
const Reporter = require('lighthouse/lighthouse-core/report/report-generator');
const fs = require('fs-extra');
const config = require('.lighthouse-config.js');

在开始使用 lighthouse 之前,首先创建一个写入文件的方法, 用于最后生成自定义的 report 文件:

async function write(file, report) {
 try {
 await fs.outputFile(file, report);
 } catch (Error e) {
 console.log("error while writing report ", e);
 }
}

调用 Lighthouse 之前,我们需要首先启动一个 Chrome 的 instance ,并将端口号提供给 Lighthouse 。--headless表示不打开 browser 窗口。

async function launchChrome() {
 let chrome;
 try {
 chrome = await chromeLauncher.launch({
 chromeFlags: [
 "--disable-gpu",
 "--no-sandbox",
 "--headless"
 ],
 enableExtensions: true,
 logLevel: "error"
 });
 console.log(chrome.port)
 return {
 port: chrome.port,
 chromeFlags: [
 "--headless"
 ],
 logLevel: "error"
 }
 } catch (e) {
 console.log("Error while launching Chrome ", e);
 }
}

Chrome 实例启动之后,我们就可以调用 Lighthouse , 调用时,须提供需要进行性能测试的网站,参数,以及前文创建好的配置,参数包含了 Chrome 启动端口,启动方式(是否 headless 等信息)。

async function lighthouseRunner(opt) {
 try {
 return await lighthouse("https://www.baidu.com", opt, config);
 } catch (e) {
 console.log("Error while running lighthouse");
 }
}

Lighthouse 的返回结果是一个包含性能测试结果, 最终版的配置参数, 指标分组等信息的 json 对象,读者可以参考 Understanding Results 获得更深入的理解。
由于这里我们需要使用 Lighthouse 官方的模板生成报告,因此调用官方提供的方法,注意第一个参数传入 result.lhr, 第二个参数声明生成 html 报告(还可以生成 csv 等格式的报告)。

function genReport(result) {
 return Reporter.generateReport(result.lhr, 'html');
}

下面我们写一个将上面几个方法串起来的函数,首先启动一个 Chrome 实例, 然后将 Chrome 实例的某些参数传递给 Lighthouse,使用 lighthouse 跑出来的结果生成报告,并写入 html 文件, html文件应带有时间戳和执行顺序作为唯一标识。start 方法返回结果中的first-meaningful-paint(这是我们最关心的指标,读者可根据自身需要替换,具体指标可参考 Lighthouse)。

async function run(timestamp, num) {
 let chromeOpt = await launchChrome();
 let result = await lighthouseRunner(chromeOpt);
 let report = genReport(result);
 await printer.write(report, 'html', `./cases/lighthouse-report@${timestamp}-${num}.html`);
 return result.lhr.audits['first-meaningful-paint'].rawValue;
 await chrome.kill();
}

下面, 我们可以正式开始写一个 gulp task 啦,首先获得当前时间戳,用于最终生成的报告命名,然后声明一个数组,用于记录每次跑 Lighthouse 生成的 first-meaningful-paint 毫秒数,然后跑10次 Lighthouse, 使用提前创建的模板文件,根据这10的结果,生成一个汇总报告,这里,笔者使用了Lighthouse对外暴露的工具函数进行字符串的替换。

gulp.task('start', async function() {
 let timestamp = Date.now();
 let spent = [];
 for(let i=0; i<5; i++) {
 spent.push(await run(timestamp, i));
 }
 let template = await fs.readFileSync('./summary/template/template.html', 'utf-8');
 let summary = Reporter.replaceStrings(template, [{
 search: '%%TIME_SPENT%%',
 replacement: JSON.stringify(spent)
 }, {
 search: '%%TIMESTAMP%%',
 replacement: timestamp
 }]);
 write(`./summary/report/summary@${timestamp}.html`, summary)
})

最后的最后, 执行:

gulp start

万事大吉。
附上汇总界面的模板源码:

<!doctype html>
<html lang="en">
<head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
 <title>Lighthouse Summary Report</title>
 <style>
 body {
 font-family: sans-serif;
 }
 table {
 margin: auto;
 }
 tr {
 border: 1px solid grey;
 }
 h1 {
 text-align: center;
 margin: 30px auto 50px auto
 }
 </style>
</head>
<body>
<table>
 <h1>Performance Summary Report</h1>
 <tr>
 <th>
 Case No.
 </th>
 <th>
 First Meaningful Paint
 </th>
 <th>
 Link To Details
 </th>
 </tr>
 <tbody id="tableBody">
 </tbody>
</table>
<script>
let timespent = %%TIME_SPENT%%;
let timestamp = %%TIMESTAMP%%;
let tableBody = document.getElementById("tableBody");
let content = '';
for(let i=0; i < timespent.length; i++) {
 content += `<tr style="border: 1px solid grey">
 <td>
 ${i+1}
 </td>
 <td>
 ${timespent[i]}
 </td>
 <td>
 <a href="../../cases/lighthouse-report@${timestamp}-${i}.html">View Details</a>
 </td>
 </tr>`
}
let total = timespent.reduce((i, j) => {
 return i + j;
})
let count = timespent.filter(function(i) { return i}).length
content += `<tr>
<td>
 AVG
</td>
<td>
${total / count}
</td>
</tr>`
tableBody.innerHTML = content;
</script>
</body>
</html>
显示全文