利用 Lighthouse 與 Puppeteer 對全站網頁做效能檢測並產生報表

Lighthouse + Puppeteer

圖片來源

本文主要是記錄如何在專案中簡易下指令來自動執行 Lighthouse 與 Puppeteer 做全站網頁的效能檢測、產生報表與其目錄。

什麼是 Lighthouse?

Lighthouse 是由 Google 開發的開源自動化檢測網站品質的工具,檢測後能給出有建設性的建議,直接告訴我們要改善什麼地方、要怎麼改,而且還有文件可查、有範例可看,能參考的資源很多,並且會將檢測結果和建議產出人機皆可閱讀的報表,可檢測效能、無障礙功能、漸進式網頁應用程式(PWA)、最佳作法和搜尋引擎最佳化(SEO,使用 Search Console 工具會更好)。

使用 Lighthouse 有以下幾種方法

這裡主要是選用 Lighthouse Node CLI 來做全站各頁的檢測,這是因為稍等會看到…要檢測的網站有些頁面是需要登入的,只靠 Lighthouse 恐怕不夠用,還需搭配 End-to-End Testing 工具才能完成工作。

為何選用 Lighthouse?

雖然能做效能檢測的工具很多,除了 Lighthouse,還有 PageSpeed InsightsWebPage TestPingdom 等,相關介紹可參考這裡…這些工具各有自己的長處,但 Lighthouse 提供 Node CLI 版本,易於整合其他自動化工具,因此就選用它了。

如何使用 Lighthouse 做效能檢測?

如前所述,由於要檢測的網站有些頁面是需要登入的,因此需要搭配 End-to-End Testing 工具做登入的動作,並導頁到要檢測的頁面來做檢測,這裡選用 Puppeteer;再來,Lighthouse 並不會幫我們做產出報表的目錄,因此我找了 Table-Builder 來協助製作目錄的表格…綜合以上需求,就必須安裝 Lighthouse、Puppeteer、Table-Builder 與 fs。

yarn add lighthouse puppeteer table-builder fs

package.json 範例

備註,除了 Puppeteer,還可選用的 End-to-End Testing 工具很多,像是 WebdriverIONightwatch 都可以…最重要考量的點,是有沒有結合 Chrome DevTools,這樣之後才能做更多客製化的效能檢測。

如何檢測需要登入的頁面?

再次,如前所述,直接使用 Lighthouse 來檢測需要登入的頁面是不可行的 因為根本看不到啊,要怎麼檢測 解決方法是先使用 Puppeteer 來做登入,而由於要借助 End-to-End Testing 工具來做登入的動作,並導頁到要檢測的頁面來做檢測,因此就要寫一支測試程式來做這些事情。

以下程式碼片段會做以下幾件事情…

程式碼片段說明如下…

async function navigateToIndex() {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  const indexURL = context['env'] === 'DEV' ? DEV_HOST : PROD_HOST;

  console.log('Navigating to index...');
  await page.goto(ENV); // (1) 到達首頁

  console.log('Starting login, entering username and password...');
  await page.click('#login'); // (2) 點擊「登入按鈕」
  await page.type('#account', 'test'); // (3) 輸入帳號
  await page.type('#password', 'test'); // (4) 輸入密碼
  await page.click('#submit'); // (5) 送出輸入的帳號、密碼

  console.log('Login success!'); // 登入成功
  return browser;
}

這樣就登入成功摟,然後稍等會導到要檢測的頁面…

如何檢測全站頁面、產生報表?

接下來,依舊使用 Puppeteer 幫我們遍歷全站網頁,並針對每個網頁用 Lighthouse 做檢測,然後產出報表。

如下程式碼,TEST_PAGES 是一個存放頁面列表的常數變數(例如:這個存放頁面列表的常數變數可能是長成這樣 [{id: '...', link: '...'}]),利用 for 迴圈將整個網站的每個頁面都跑一遍;當中使用 Lighthouse 檢測,這裡會產出兩種格式的報表-HTML 與 JSON,再利用 Lighthouse 內建的 reportGenerator 畫出報表;最後使用 TableBuilder 做總結,產生一份簡單的目錄來作 report cover。

以下程式碼片段會做以下幾件事情…

程式碼片段說明如下…

browser = await navigateToIndex();
page = (await browser.pages())[0];
const records = []; // (1) 存放所有的 overview 紀錄,等等做目錄用

console.log('Running Lighthouse...');

for (let i = 0; i < TEST_PAGES.length; i += 1) {
  // (2) 將整個網站的每個頁面跑一次
  const report = await lighthouse(
    `${ENV}${TEST_PAGES[i].link}`,
    {
      // lighthouse 檢測中...
      port: new URL(browser.wsEndpoint()).port,
      output: 'json',
      logLevel: 'info',
      disableDeviceEmulation: true,
      chromeFlags: ['--disable-mobile-emulation'],
    },
    config,
  );

  // (3) 產出兩種格式的報表-HTML 與 JSON
  const json = reportGenerator.generateReport(report.lhr, 'json');
  const html = reportGenerator.generateReport(report.lhr, 'html');
  const categories = Object.values(report.lhr.categories);
  const record = {};

  console.log(`Review results for ${TEST_PAGES[i].id}...`);
  record[
    'Page'
  ] = `<a href='./lighthouse-results-${TEST_PAGES[i].id}.html' target='blank'>${TEST_PAGES[i].id}</a>`;

  // (4) overview 所需的資料,包含 「Performance」、「Accessibility」、「Best Practices」、「SEO」、「Progressive Web App」五個項目與其分數,分數以小數點表示,滿分為 1
  for (let i = 0; i < categories.length; i += 1) {
    const key = `${categories[i].title}`;
    record[key] = categories[i].score;
    console.log(`Lighthouse scores: ${categories[i].title} ${categories[i].score}`);
  }

  records.push(record);

  // (5) 產生每頁的報表
  console.log('Writing results...');
  fs.writeFileSync(`reports/performance/lighthouse-results-${TEST_PAGES[i].id}.json`, json);
  fs.writeFileSync(`reports/performance/lighthouse-results-${TEST_PAGES[i].id}.html`, html);
}

製作目錄

報表產生好了,但總需要有目錄和簡述到底成果如何吧?因此這裡會做一個簡單的目錄,並列出每一頁「Performance」、「Accessibility」、「Best Practices」、「SEO」、「Progressive Web App」五個項目的個別分數。

以下程式碼片段會做以下幾件事情…

程式碼片段說明如下…

// (1) 設定表頭,包含 「Performance」、「Accessibility」、「Best Practices」、「SEO」、「Progressive Web App」五個項目
const headers = {
  Page: 'Page',
  Performance: 'Performance',
  Accessibility: 'Accessibility',
  'Best Practices': 'Best Practices',
  SEO: 'SEO',
  'Progressive Web App': 'Progressive Web App',
};

// (2) 使用 TableBuilder 產生一份簡單的目錄
const table = new TableBuilder({ class: 'repor-table' })
  .setHeaders(headers) // see above json headers section
  .setData(records) // see above json data section
  .render();

// (3) 將產出的結果寫入 HTML 檔案
fs.writeFileSync(`reports/performance/lighthouse-report-index.html`, table);

產生的目錄如下圖所示(很陽春,待改善 ╮(╯_╰)╭)。

目錄

點此看產生的目錄檔範例。

完整程式碼可參考這裡

YARN / NPM Scripts

這裡寫了幾種檢測效能的 script-檢測單一頁面、依照開發或正式機環境做檢測。

指定單一網址做檢測,yarn lighthouse 後可接受一個網址。注意,這只適用於不用登入的頁面。

yarn lighthouse https://next-meal.vercel.app/

依照開發環境做檢測,由於有搭配 End-to-End Testing 工具來做登入,因此適用需要登入的頁面,且能做全站網頁檢測。

yarn perf-dev

依照正式機環境做檢測,同上,由於有搭配 End-to-End Testing 工具來做登入,因此適用需要登入的頁面,且能做全站網頁檢測。

yarn perf-prod

package.json 的設定範例如下,或參考這裡

"lighthouse": "./node_modules/lighthouse/lighthouse-cli/index.js --output-path=./reports/performance/lighthouse-results.html $url",
"perf-dev": "node test/performance/index.js --env=DEV",
"perf-prod": "node test/performance/index.js --env=PROD"

疑難雜症

Lighthouse 若不做任何設定,則預設的測試環境是手機(mobile),因此若是想測桌機(desktop),記得在使用 node module 要設定 emulatedFormFactor 為 desktop(若使用 cli 則是 --emulated-form-factor)才能準確測試。Lighthouse 的預設滿多的…像是 throttling 相關,可參考官方文件

預設是 Moto G4。

Lighthouse CLI options default value

因此是手機畫面截圖。

Lighthouse mobile preview


由於 emulatedFormFactor 改為 desktop,因此「Device」是「Emulated Desktop」。

Lighthouse CLI options for desktop

畫面截圖是桌機。

Lighthouse desktop preview

後續工作

之後會有文章來記錄

等等的文章,敬請期待 (๑•̀ㅂ•́)و✧

參考資料


Lighthouse Puppeteer End-to-End Testing 端對端測試 lighthouse-cli 自動化測試 效能調校 吃什麼 前端效能 系列文