NodeJS + Lighthouse + Gulp 搭建自動化網站性能測試工具

假設你還不知道Lighthouse是什麼

Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, and more.

You can run Lighthouse in Chrome DevTools, from the command line, or as a Node module. You give Lighthouse a URL to audit, it runs a series of audits against the page, and then it generates a report on how well the page did. From there, use the failing audits as indicators on how to improve the page. Each audit has a reference doc explaining why the audit is important, as well as how to fix it.html

Lighthouse 是Google公司旗下一個開源的、可自動化檢測網站質量的工具,界面友好、操做簡單、使用方式多樣、視角全面,能夠用它來測試任意網頁,普通用戶、QA、開發均可以快速上手。node

啓動姿式

難度係數 +1

使用Lighthouse的方式有不少種,最簡單的,可使用 Chrome 的開發者工具,步驟以下:git

  1. 打開 Chrome 瀏覽器
  2. 按F12
  3. 在彈出來的窗口中打開 audits 標籤
  4. 點擊 Perform an audit...勾選所有
  5. Run audit

難度係數+2

也可使用命令行。github

  1. 安裝Node
  2. 安裝Lighthouse npm install -g lighthouse
  3. 在命令行中run lighthouse <url>

以上兩種使用方式都不是本文的重點,若是想深刻了解,可參照 Run Lighthouse in DevToolsweb

難度係數+3

因爲最近在學習 NodeJS, 所以筆者決定使用 Node 8 + Gulp 來跑 lighthouse,爲了提升結果的準確性,每次task都跑10次 lighthouse, 而且只關心結果指標中的 first-meaningful-paint 毫秒數,最終取10次的平均值,爲了可視化與可讀性,最終的結果以網頁的形式展現,用戶可在網頁上看到每次執行 Lighthouse 以後 first-meaningful-paint 的毫秒數,也能夠看到平均值,若是用戶對某次執行的細節感興趣,能夠點擊連接察看。最終的結果長這個樣子:chrome

clipboard.png

clipboard.png

環境搭建

安裝 Node 8npm

安裝依賴包json

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'gulp

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 (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>

源碼地址

把最重要的放到最後 附上github源碼
lighthouse-node

相關文章
相關標籤/搜索