【前端性能測試】不服?那就跑個分!!!

希沃ENOW大前端javascript

公司官網:CVTE(廣州視源股份)html

團隊:CVTE旗下將來教育希沃軟件平臺中心enow團隊前端

本文做者:java

小羽名片.png

hello,你們好,我是小羽同窗。一個平凡,而又不甘於平凡的前端開發工程師!git

你們對跑分應該挺熟悉了吧?尤爲是米粉,雷老闆的那句不服?那就跑個分!應該歷歷在目吧?哈哈哈程序員

在平常的前端開發過程當中,小夥伴們或多或少都有接觸過性能優化吧?可是你日常是怎麼肯定性能是否獲得了提高呢?Google的開源工具——Lighthouse也就應運而生。github

Lighthouse主要是用於分析網絡應用和網頁,收集現代性能指標並提供對開發人員最佳實踐的意見。對於前端開發工程師來講,能夠簡潔明瞭得看到項目中的不足之處,以及優化的方法。是平常開發中不可多得的神器呀!web

簡單的使用

lighthouse 插件安裝

chrome的商店中,搜索lighthouse,而後添加。chrome

設置測試項目

而後在chrome頂部的插件欄中打開插件。點擊設置圖標,還能夠選擇進行測試(跑分)的項目,以及是測試pc端仍是手機端的網頁。npm

測試(跑分)

而後咱們直接點擊generate report按鈕便可直接進入測試,過一段時間後結果就會出來啦。下面那個圖就是咱們ENOW 大前端團隊的掘金首頁測試評分啦,能夠看到掘金seo優化作的仍是很是棒的,哈哈哈~

整個測試報告中給咱們標明瞭頁面中的各類性能相關的參數,而後也給到了咱們不少相關的優化建議。小夥伴們以爲感興趣的話,能夠深刻的去了解各個參數哦~

image.png

如今性能測試工具備是有了,可是有沒有發現這個就只能一個一個頁面的去測,好麻煩呀。若是有幾十個頁面還得一個一個的去點。大家會不嫌麻煩嘛?反正像小羽同窗這麼厭倦重複性工做的人來講,早就煩死了  (╯°Д°)╯︵┻━┻

如今問題來了,那有沒有什麼方便的方法呀?一個一個的去點着測試總不是方法呀。

彆着急,乾貨立刻就到啦~

其實除了chrome插件的使用方式,咱們還可使用命令行的方式來調用Lighthouse

1.全局安裝 lighthouse

npm install -g lighthouse
複製代碼

2.輸入你的頁面

lighthouse http://test.com
複製代碼

小羽在這裏就不進行展現了,我們直接進入主題吧,嘿嘿~

製做跑分工具

咱們要製做的前端性能跑分工具,主要是藉助於lighthouse的這個npm包以及gulp腳本。

1.初始化項目

新建一個文件夾,小羽這裏是enow-lighthouse,而後新建package.json並寫入相關的內容,而後cnpm install安裝咱們編寫工具時所須要的一些依賴包

{
  "name": "enow-lighthouse",
  "version": "1.0.0",
  "description": "ENOW大前端——lighthouse測試工具",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"gulp start"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chrome-launcher": "^0.13.4",
    "del": "^6.0.0",
    "fs-extra": "^9.1.0",
    "gulp": "^4.0.2",
    "lighthouse": "^7.3.0"
  }
}
複製代碼

2.編寫gulp腳本

1.在根目錄下建立gulpfile.js

而後在gulpfile.js中寫入如下的一段代碼,先測試一下咱們的項目能不能正常跑起來。若是正常的話,則會顯示以下圖的ENOW 大前端~

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 del = require("del")
let chrome

gulp.task("start",async function(cb){
  console.log("ENOW 大前端")
  cb()
})
複製代碼

2.新增launchChrome方法,該方法是用來啓動chrome的,並返回須要用到的chrome信息

  • --headless 表示不打開browser窗口
  • --disable-gpu 表示不開啓gpu
  • --no-sandbox 表示不開啓沙箱模式
// 開啓chrome
async function launchChrome() {
  try {
      chrome = await chromeLauncher.launch({
          chromeFlags: [
              "--disable-gpu",
              "--no-sandbox",
              "--headless"
          ],
          enableExtensions: true,
          logLevel: "error"
      });
      return {
          port: chrome.port,
          chromeFlags: [
              "--headless"
          ],
          logLevel: "error"
      }
  } catch (e) {
      console.log("ENOW lighthouse error: launching Chrome ", e);
  }
}
複製代碼

3.新增lighthouseRunner方法,該方法是用來跑lighthouse測試的,並返回測試結果

// 啓動lighthouse測試
async function lighthouseRunner(url, opt, config={extends: 'lighthouse:default'}) {
  try {
      return await lighthouse(url, opt, config);
  } catch (e) {
      console.log("ENOW lighthouse error: running lighthouse");
  }
}
複製代碼

4.新增genReport方法,該方法是用來獲取當前頁面報告的html頁面,並返回生成的html頁面

// 生成當前頁面的報告
function genReport(result) {
  return Reporter.generateReport(result.lhr, 'html');
}
複製代碼

5.新增run方法,該方法是每一個頁面的測試入口

// 每一個頁面的測試入口
async function run(url, timestamp, config) {
  let chromeOpt = await launchChrome();
  let result = await lighthouseRunner(url, chromeOpt, config);
  let report = genReport(result);
  // 保存報告
  await printer.write(report, 'html', `./cases/lighthouse-report@${timestamp}.html`);
  // 關閉chrome
  await chrome.kill();
  return
}
複製代碼

6.修改gulp.task,而後在根目錄下新建cases文件夾,而後運行npm run start等待一段時間後,就會發如今咱們剛剛新建的cases文件夾中生成了咱們想要的性能測試報告啦~

gulp.task("start",async function(cb){
  let taskList = [
    `https://juejin.cn/`,
    `https://juejin.cn/`,
    `https://juejin.cn/`,
  ]
  for(let item of taskList){
    let timestamp = Date.now();
    await run(item,timestamp)
  }
  cb()
})
複製代碼

小羽,你在這裏洋洋灑灑的寫了一大堆東西,咱看着腦袋都疼了!!!

其實這裏看着挺多東西的,可是邏輯仍是很容易理解滴~

待小羽給小夥伴們分析一波:

  • 首先咱們運行npm run start就會調用gulp start,接着就進入到了gulp.task()
  • 而後gulp.task()是遍歷taskList,而後調用run方法(每一個頁面的測試入口)
  • run方法調用launchChrome(),而後返回chrome相關信息
  • run方法調用lighthouseRunner(),而後返回測試結果
  • run方法調用genReport(),返回生成的html頁面
  • run方法html頁面寫入到文件中
  • 關閉chrome

目前爲止,gulpfile.js中的代碼以下

/* * @Description: * @Author: 小羽 * @LastEditors: 小羽 * @Date: 2021-04-11 23:05:22 * @LastEditTime: 2021-04-12 00:30:55 */
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 del = require("del")
let chrome

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


// 啓動lighthouse測試
async function lighthouseRunner(url, opt, config={extends: 'lighthouse:default'}) {
  try {
      return await lighthouse(url, opt, config);
  } catch (e) {
      console.log("ENOW lighthouse error: running lighthouse");
  }
}

// 獲取當前頁面的報告
function genReport(result) {
  return Reporter.generateReport(result.lhr, 'html');
}

// 每一個頁面的測試入口
async function run(url, timestamp, config) {
  let chromeOpt = await launchChrome();
  let result = await lighthouseRunner(url, chromeOpt, config);
  let report = genReport(result);
  // 保存報告
  await printer.write(report, 'html', `./cases/lighthouse-report@${timestamp}.html`);
  // 關閉chrome
  await chrome.kill();
  return
}

gulp.task("start",async function(cb){
  let taskList = [
    `https://juejin.cn/`,
    `https://juejin.cn/`,
    `https://juejin.cn/`,
  ]
  for(let item of taskList){
    let timestamp = Date.now();
    await run(item,timestamp)
  }
  cb()
})
複製代碼

3.抽離任務列表

通常來講,咱們的任務列表是不會直接寫在方法中,爲了符合低耦合高內聚編程的思路,咱們單獨把咱們的任務列表抽離出來,而後使用require引入到gulpfile.js中。在根目錄下新建taskList.js

當咱們須要測某個頁面的性能時,就能夠把taskList.js所有改成同一個url就好。若是想測網站的總體性能,就把網站全部的URL拷貝進去就ok啦。

想知道怎麼獲取評分平均值以及總報告的小夥伴,請移步下一小節   (✧◡✧)

// taskList.js
module.exports = [
  `https://juejin.cn/`,
  `https://juejin.cn/`,
  `https://juejin.cn/`,
]
複製代碼
// gulpfile.js
const taskList = require("./taskList")

// 省略中間的代碼。。。

gulp.task("start",async function(cb){
  for(let item of taskList){
    let timestamp = Date.now();
    await run(item,timestamp)
  }
  cb()
})
複製代碼

4.生成總報告

雖說咱們如今的工程也能夠一鍵跑分了,可是有沒有發現生成的文件不少,並且都得一個一個的點擊進去才能看到咱們的頁面信息,可不能夠?

stop,別問,問就是成妾作不到!!!

哈哈哈,逗下大家啦,程序員除了產品經理提出的需求完成不了外,其餘時候都是超厲害滴【手動狗頭】

修改一下代碼,

npm run start啓動跑分程序,輸出一下lighthouse跑分後的結果來瞧瞧先。

在咱們的根目錄下就會生成一個file.txt文件。打開後一看。。。這啥玩意?徹底無法看呀,這可咋辦?

別急,山人自有妙計

打開咱們原來生成的報表,稍微分析一下就會發現。file.txt中,lhr字段中輸出的數據其實就是咱們控制檯中輸出的數據。那就看控制檯中的數據得了唄【狗頭】

新增一個write()方法,功能是輸出到文件中,這裏是生成咱們的總報告

// 生成總報告
async function write(file, report) {
  try {
      await fs.outputFile(file, report);
      return true
  } catch (e) {
      console.log("error while writing report ", e);
  }
}
複製代碼

修改run方法,run方法中返回的數據,小夥伴們想返回那些字段本身作抉擇就ok啦~

async function run(url, timestamp, num, config) {
  let chromeOpt = await launchChrome();
  let result = await lighthouseRunner(url, chromeOpt, config);
  let report = genReport(result);
  // 保存報告
  await printer.write(report, 'html', `./cases/lighthouse-report@${timestamp}-${num}.html`);
  result.lhr.audits['first-contentful-paint'].rawValue;
  let res = {
      audits:{
          "first-contentful-paint":result.lhr.audits['first-contentful-paint']
      },
      categories:result.lhr.categories,
      lighthouseVersion:result.lhr.lighthouseVersion,
      requestedUrl:result.lhr.requestedUrl
  }
  // 關閉chrome
  await chrome.kill();
  return res;//result.lhr
}
複製代碼

根目錄下新增summary/template/template.htmltemplate.html是咱們的總報告模板文件,小羽這裏也是隨便寫寫,小夥伴們能夠自由發揮~

修改gulp.task()

gulp.task("start",async function(cb){
  let timestamp = Date.now();
  let spent = [];
  console.log(`共 ${taskList.length} 個任務`)
  for (let i = 0; i < taskList.length; i++) {
    console.log(`當前第 ${i+1} 個任務`)
    spent.push(await run(taskList[i], 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
  }]);
  await write(`./summary/report/summary@${timestamp}.html`, summary)
  cb()
})
複製代碼

咳咳咳,提起精神來,看看咱們的成果。

npm run start後,會發如今咱們的summary/report中生成了一個新的html文件,我們打開看下

5.添加PC端和移動端的命令

emmm,不對,還有問題,由於咱們一直都是測的移動端,那咱們怎麼測試pc端呀???總不能每測試一次就去修改一次配置吧?

根目錄下新增constants.jslighthouse-desktop-config.js(pc端)lighthouse-mobile-config.js(移動端)

既然把移動端和pc端分開了,那原來的gulp.task()也就只有一個,不夠用了,那就多加一個吧。而後方便區分,再修改一下名字爲create:report-desktopcreate:report-mobile。修改gulpfile.jspackage.json

// gulpfile.js
const desktopConfig = require('./lighthouse-desktop-config.js');
const mobileConfig = require('./lighthouse-mobile-config.js');


// 省略部分代碼。。。

gulp.task('create:report-desktop',async function(cb){
  let timestamp = Date.now();
  let spent = [];
  console.log(`共 ${taskList.length} 個任務`)
  for (let i = 0; i < taskList.length; i++) {
    console.log(`當前第 ${i+1} 個任務`)
    spent.push(await run(taskList[i], timestamp, i , desktopConfig));
  }
  // 替換模板中的內容
  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
  }]);
  await write(`./summary/report/summary@${timestamp}.html`, summary)
  cb()
})

gulp.task('create:report-mobile',async function(cb){
  let timestamp = Date.now();
  let spent = [];
  console.log(`共 ${taskList.length} 個任務`)
  for (let i = 0; i < taskList.length; i++) {
    console.log(`當前第 ${i+1} 個任務`)
    spent.push(await run(taskList[i], timestamp, i, mobileConfig));
  }
  // 替換模板中的內容
  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
  }]);
  await write(`./summary/report/summary@${timestamp}.html`, summary)
  cb()
})
複製代碼
// package.json

{
  "name": "enow-lighthouse",
  "version": "1.0.0",
  "description": "ENOW大前端——lighthouse測試工具",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "create:report-desktop":"gulp create:report-desktop",
    "create:report-mobile":"gulp create:report-desktop"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chrome-launcher": "^0.13.4",
    "del": "^6.0.0",
    "fs-extra": "^9.1.0",
    "gulp": "^4.0.2",
    "lighthouse": "^7.3.0"
  }
}
複製代碼

此時,咱們已經沒有了start命令了,因此啓動的命令就變成了npm run create:report-desktopnpm run create:report-mobile

6.刪除舊的測試文件

可是小夥伴們有沒有發現咱們的報表文件每次跑都會遺留下來,越積越多呀?

gulpfile.js中新增三個gulp.task()。而後修改package.json中命令

  • npm run mobile:清理文件,而後執行移動端跑分
  • npm run desktop:清理文件,而後執行pc端跑分
  • npm run clean:清理文件
  • npm run create:report-mobile:執行移動端跑分
  • npm run create:report-desktop:執行pc端跑分

這裏簡單的說一下,在gulp中,gulp.series()是按照順序執行的,每次執行一個。而gulp.paralle(),則是併發運行的。

// gulpfile.js
// 清理數據
gulp.task('clean:report', function (cb) {
  del([
      'cases/**/*',
      'summary/report/**/*',
  ], cb);
  cb()
});

// gulp.series:按照順序執行
// gulp.paralle:能夠並行計算
gulp.task("start-desktop", gulp.series("clean:report","create:report-desktop"), function () {})
gulp.task("start-mobile", gulp.series("clean:report","create:report-mobile"), function () {})
複製代碼
// package.json
{
  "name": "enow-lighthouse",
  "version": "1.0.0",
  "description": "ENOW大前端——lighthouse測試工具",
  "main": "index.js",
  "scripts": {
    "mobile":"gulp start-mobile",
    "desktop":"gulp start-desktop",
    "clean":"gulp clean:report",
    "create:report-desktop":"gulp create:report-desktop",
    "create:report-mobile":"gulp create:report-desktop"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chrome-launcher": "^0.13.4",
    "del": "^6.0.0",
    "fs-extra": "^9.1.0",
    "gulp": "^4.0.2",
    "lighthouse": "^7.3.0"
  }
}
複製代碼

好了,如今咱們整個跑分工具就製做完成啦。若是有小夥子說他寫的網頁有多厲害,那就二話不說,掏出這個跑分工具,一決高下吧~

後語

本文主要是結合Google的開源項目Lighthousegulp腳本編寫了一個前端性能跑分工具。主要用來幫助前端開發工程師可以更加全面的瞭解本身的網站/項目,快速找出優缺點,以及能夠改善的方向。

相關文章
相關標籤/搜索