希沃ENOW大前端
公司官网:CVTE(广州视源股份)
团队:CVTE旗下未来教育希沃软件平台中心enow团队
本文作者:
hello
,大家好,我是小羽同学。一个平凡,而又不甘于平凡的前端开发工程师!
大家对跑分应该挺熟悉了吧?尤其是米粉,雷老板的那句不服?那就跑个分!
应该历历在目吧?哈哈哈
在日常的前端开发过程中,小伙伴们或多或少都有接触过性能优化吧?但是你平常是怎么确定性能是否得到了提升呢?Google
的开源工具——Lighthouse
也就应运而生。
Lighthouse
主要是用于分析网络应用和网页,收集现代性能指标并提供对开发人员最佳实践的意见。对于前端开发工程师来说,可以简洁明了得看到项目中的不足之处,以及优化的方法。是日常开发中不可多得的神器呀!
简单的使用
lighthouse 插件安装
chrome
的商店中,搜索lighthouse
,然后添加。
设置测试项目
然后在chrome
顶部的插件栏中打开插件。点击设置图标,还可以选择进行测试(跑分)的项目,以及是测试pc
端还是手机端的网页。
测试(跑分)
然后我们直接点击generate report
按钮即可直接进入测试,过一段时间后结果就会出来啦。下面那个图就是我们ENOW 大前端团队
的掘金首页测试评分啦,可以看到掘金
的seo
优化做的还是非常棒的,哈哈哈~
整个测试报告中给我们标明了页面中的各种性能相关的参数,然后也给到了我们很多相关的优化建议。小伙伴们觉得感兴趣的话,可以深入的去了解各个参数哦~
现在性能测试工具有是有了,但是有没有发现这个就只能一个一个页面的去测,好麻烦呀。如果有几十个页面还得一个一个的去点。你们会不嫌麻烦嘛?反正像小羽同学这么厌倦重复性工作的人来说,早就烦死了 (╯°Д°)╯︵┻━┻
现在问题来了,那有没有什么方便的方法呀?一个一个的去点着测试总不是方法呀。
别着急,干货马上就到啦~
其实除了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.html
,template.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.js、lighthouse-desktop-config.js(pc端)、lighthouse-mobile-config.js(移动端)
既然把移动端和pc端分开了,那原来的gulp.task()也就只有一个,不够用了,那就多加一个吧。然后方便区分,再修改一下名字为create:report-desktop
和create:report-mobile
。修改gulpfile.js
,package.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-desktop
和npm 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
的开源项目Lighthouse
和 gulp脚本
编写了一个前端性能跑分工具。主要用来帮助前端开发工程师能够更加全面的了解自己的网站/项目,快速找出优缺点,以及可以改善的方向。