自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

探究 LightHouse 工作流程

開(kāi)發(fā) 開(kāi)發(fā)工具
Chrome DevTools 協(xié)議允許使用工具來(lái)檢測(cè)、檢查、調(diào)試和分析 Chromium、Chrome 和其他基于 Blink 的瀏覽器。 在Chrome擴(kuò)展中,Chrome protocol 利用 chrome.debugger Api 通過(guò) WebSocket[3] 來(lái)建立連接。

本文為來(lái)自 字節(jié)教育-智能學(xué)習(xí)-前端團(tuán)隊(duì) 的文章,已授權(quán) ELab 發(fā)布。

智能學(xué)習(xí)前端團(tuán)隊(duì) 自創(chuàng)立以來(lái),團(tuán)隊(duì)專注于打破大眾對(duì)教育的刻板印象,突破固有的教學(xué)思維,攻破各類教學(xué)屏障。旨在為每一位學(xué)生制定最合適的學(xué)習(xí)方案,予以因材施教,使優(yōu)質(zhì)教育隨‘觸’可達(dá)。

什么是 Lighthouse

Lighthouse analyzes web apps and web pages, collecting modern performance metrics and insights on developer best practices.

圖片

使用方式

  • Chrome 瀏覽器插件。Chrome 插件的形式提供了更加友好的用戶界面,方便讀取報(bào)告。
  • Chrome DevTools。該工具集成在最新版本的 Chrome 瀏覽器中,無(wú)需安裝即可使用。
  • Lighthouse CLI 命令行工具。方便將 Lighthouse 集成到持續(xù)集成系統(tǒng)中。
  • 代碼中引用。我們也能通過(guò) Node.js 模塊引入 Lighthouse 工具包。

原理結(jié)構(gòu)[1]

圖片

圖片

Gathering

Driver 驅(qū)動(dòng)

通過(guò) Chrome Debugging Protocol 和 Puppeteer[2] (提供無(wú)頭瀏覽器環(huán)境模擬頁(yè)面操作) /進(jìn)行交互。

Chrome Debugging Protocol(CDP)

Chrome DevTools 協(xié)議允許使用工具來(lái)檢測(cè)、檢查、調(diào)試和分析 Chromium、Chrome 和其他基于 Blink 的瀏覽器。 在Chrome擴(kuò)展中,Chrome protocol 利用 chrome.debugger Api 通過(guò) WebSocket[3] 來(lái)建立連接。

Instrumentation 分為多個(gè) Domains(DOM, Debugger, Network 等)。每個(gè) Domain 定義了許多它支持的命令和它生成的事件。命令和事件都是固定結(jié)構(gòu)的序列化 JSON 對(duì)象。

圖片

CDP Domains,紅色為實(shí)驗(yàn)性

Domain 必須 enable() 后才可以發(fā)出事件。一旦啟用enable,它們將刷新表示狀態(tài)的所有事件。因此,網(wǎng)絡(luò)事件僅在 enable() 后才會(huì)發(fā)出。所有協(xié)議代理解析 enable() 的回調(diào)。比如:

// will NOT work
driver.defaultSession.sendCommand('Security.enable').then(_ => {
driver.defaultSession.on('Security.securityStateChanged', state => { /* ... */ });
})

// WILL work! happy happy. :)
driver.defaultSession.on('Security.securityStateChanged', state => { /* ... */ }); // event binding is synchronous
driver.defaultSession.sendCommand('Security.enable');

調(diào)試協(xié)議:閱讀更好地調(diào)試協(xié)議Better debugging of the Protocol[4]。

配置passes

passes 屬性控制如何加載請(qǐng)求的 URL,以及在加載時(shí)收集哪些關(guān)于頁(yè)面的信息。pass 數(shù)組中的每個(gè)條目代表頁(yè)面的一次加載,

每個(gè) pass 都定義了基本設(shè)置,例如等待頁(yè)面加載多長(zhǎng)時(shí)間、是否記錄 trace 文件。此外,每次傳遞都定義了要使用的 gatherer 列表。gatherer 可以從頁(yè)面中讀取信息以生成 artifacts,稍后 Audits 使用這些artifacts提供 Lighthouse 報(bào)告。

具體的 pass 配置示例:

{
passes: [{
passName: 'fastPass',
atherers: ['fast-gatherer'],
},
{
passName: 'slowPass',
recordTrace: true,
useThrottling: true,
networkQuietThresholdMs: 5000,
gatherers: ['slow-gatherer'],
}]
}

Gatherers 采集器

決定在頁(yè)面加載過(guò)程中采集哪些信息,將采集的信息輸出為 artifacts。使用 Driver 采集頁(yè)面信息。用 --gather-mode 指令運(yùn)行可以獲得3個(gè)采集產(chǎn)物:

artifacts.json: 所有采集器的輸出。

defaultPass.trace.json: 大多數(shù)性能指標(biāo)??梢栽贒evTools性能面板中查看。

defaultPass.devtoolslog.json: DevTools Protocol[5] 事件的日志。

每一個(gè) gatherer繼承自相同的基類 Gatherer,基類 Gatherer定義了傳遞生命周期的n個(gè)方法。 gatherer的artifacts是生命周期方法返回的最后一個(gè)未定義值,所有方法都可以直接返回artifacts或返回解析為該值的 Promise。子類只需實(shí)現(xiàn)生命周期方法即可。

比如用于js覆蓋率的 gatherer:

該實(shí)例實(shí)現(xiàn)了 startInstrumentation 、stopInstrumentation、getArtifact 3個(gè)生命周期方法,其

class JsUsage extends FRGatherer {
meta = {
supportedModes: ['snapshot', 'timespan', 'navigation'],
};

constructor() {
super();
this._scriptUsages = [];
}
async startInstrumentation(context) {
const session = context.driver.defaultSession;
await session.sendCommand('Profiler.enable');
await session.sendCommand('Profiler.startPreciseCoverage', {detailed: false});
}


async stopInstrumentation(context) {
const session = context.driver.defaultSession;
const coverageResponse = await session.sendCommand('Profiler.takePreciseCoverage');
this._scriptUsages = coverageResponse.result;
await session.sendCommand('Profiler.stopPreciseCoverage');
await session.sendCommand('Profiler.disable');
}

async getArtifact() {
const usageByScriptId = {};
for (const scriptUsage of this._scriptUsages) {
if (scriptUsage.url === '' || scriptUsage.url === '_lighthouse-eval.js') {
continue;
}
usageByScriptId[scriptUsage.scriptId] = scriptUsage;
}
return usageByScriptId;
}
}

class FRGatherer { meta = { supportedModes : []}; //在任意時(shí)間段內(nèi)開(kāi)始觀察頁(yè)面 startInstrumentation ( passContext ) { } //Sensitive開(kāi)始觀察頁(yè)面 startSensitiveInstrumentation ( passContext ) { } //Sensitive停止觀察頁(yè)面的方法 stopSensitiveInstrumentation ( passContext ) { } //在任意時(shí)間段內(nèi)結(jié)束觀察頁(yè)面 stopInstrumentation ( passContext ) { } //收集有關(guān)頁(yè)面的結(jié)果 getArtifact ( passContext ) { } /** * Legacy */ get name () {} async beforePass ( passContext ) {} pass ( passContext ) { } async afterPass ( passContext, loadData ) {}

當(dāng) pass 中定義的所有 gatherers 運(yùn)行完后,就會(huì)生成一個(gè)中間產(chǎn)物 artifacts,此后 Lighthouse 就可以斷開(kāi)與瀏覽器的連接,只使用 artifacts 進(jìn)行后續(xù)的分析。

Trace 鏈路追蹤

core/lib/tracehouse/trace-processor.js提供了鏈路到更有意義對(duì)象的轉(zhuǎn)換。每個(gè)原始trace event[6] 都具有以微秒為單位增長(zhǎng)的時(shí)間戳、線程ID、進(jìn)程ID、持續(xù)時(shí)間以及其他適用的元數(shù)據(jù)屬性(比如事件類型、任務(wù)名稱、幀等)

Example Trace Event

{
'pid': 41904, // process ID
'tid': 1295, // thread ID
'ts': 1676836141, // timestamp in microseconds
'ph': 'X', // trace event type
'cat': 'toplevel', // trace category from which this event came
'name': 'MessageLoop::RunTask', // relatively human-readable description of the trace event
'dur': 64, // duration of the task in microseconds
'args': {}, // contains additional data such as frame when applicable
}

圖片

Processed trace

Processed trace 可識(shí)別關(guān)鍵時(shí)刻的 trace 事件((navigation start, FCP, LCP, DCL, trace end 等),并過(guò)濾出主進(jìn)程和主線程事件的視圖。

{
processEvents: [/* all trace events in the main process */],
mainThreadEvents: [/* all trace events on the main thread */],
timings: {
timeOrigin: 0, // timeOrigin is always 0
msfirstContentfulPaint: 150, // firstContentfulPaint time in ms after time origin
/* other key moments */
traceEnd: 16420, // traceEnd time in ms after time origin
},
timestamps: {
timeOrigin: 623000000, // timeOrigin timestamp in microseconds, marks the start of the navigation of interest
firstContentfulPaint: 623150000, // firstContentfulPaint timestamp in microseconds
/* other key moments */
traceEnd: 639420000, // traceEnd timestamp in microseconds
},
}

實(shí)現(xiàn)

  1. Connecting to browser
  2. Resetting state with about:blank
  3. Navigate to about:blank
  4. Benchmarking machine
  5. Initializing…
  6. Preparing target for navigation mode
  7. Running defaultPass pass
  8. Resetting state with about:blank
  9. Navigate to about:blank
  10. Preparing target for navigation
  11. Cleaning origin data
  12. Cleaning browser cache
  13. Preparing network conditions
  14. Beginning devtoolsLog and trace
  15. Loading page & waiting for onload
  16. Navigating to https:XXX
  17. Gathering in-page: XXXXXXXX. (xN)
  18. Gathering trace
  19. Gathering devtoolsLog & network records
  20. Gathering XXX (xN)
begin();
|
→ runLighthouse();
|
→ legacyNavigation();


async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
//...
const connection = userConnection || new CriConnection(flags.port, flags.hostname);
const artifacts = await Runner.gather(() => {
const requestedUrl = UrlUtils.normalizeUrl(url);
return Runner._gatherArtifactsFromBrowser(requestedUrl, options, connection);
}, options);
return Runner.audit(artifacts, options);
}

static async _gatherArtifactsFromBrowser(requestedUrl, runnerOpts, connection) {
//創(chuàng)建connection的Driver
const driver = runnerOpts.driverMock || new Driver(connection);
const gatherOpts = {
driver,
requestedUrl,
settings: runnerOpts.config.settings,
computedCache: runnerOpts.computedCache,
};
const artifacts = await GatherRunner.run(runnerOpts.config.passes, gatherOpts);
return artifacts;
}
/****** GatherRunner ****/
static async run(passConfigs, options) {


//1.Connecting to browser
//通過(guò) Websocket 建立連接, 基于 Chrome Debugging Protocol 通信
// CDPSession 實(shí)例用于與 Chrome Devtools 協(xié)議的原生通信
await driver.connect();
// 在 devtools/extension 案例中,我們?cè)趪L試清除狀態(tài)時(shí)仍不能在站點(diǎn)上
// 所以我們首先導(dǎo)航到 about:blank,然后應(yīng)用我們的仿真和設(shè)置
// 2.Resetting state with about:blank & 3.Navigating to blankPage
await GatherRunner.loadBlank(driver);
// 4. Benchmarking machine
const baseArtifacts = await GatherRunner.initializeBaseArtifacts(options);

// ...processing benchmarkIndex

// 5. Initializing…
await GatherRunner.setupDriver(driver, options);

let isFirstPass = true;
// each pass
for (const passConfig of passConfigs) {
const passContext = {
gatherMode: 'navigation',
driver,
url: options.requestedUrl,
settings: options.settings,
passConfig,
baseArtifacts,
computedCache: options.computedCache,
LighthouseRunWarnings: baseArtifacts.LighthouseRunWarnings,
};
//Starting from about:blank, load the page and run gatherers for this pass.
const passResults = await GatherRunner.runPass(passContext);
Object.assign(artifacts, passResults.artifacts);

// If we encountered a pageLoadError, don't try to keep loading the page in future passes.
if (passResults.pageLoadError && passConfig.loadFailureMode === 'fatal') {
baseArtifacts.PageLoadError = passResults.pageLoadError;
break;
}

if (isFirstPass) {
await GatherRunner.populateBaseArtifacts(passContext);
isFirstPass = false;
}
}

await GatherRunner.disposeDriver(driver, options);
return finalizeArtifacts(baseArtifacts, artifacts);
} catch (err) {
// Clean up on error. Don't await so that the root error, not a disposal error, is shown.
GatherRunner.disposeDriver(driver, options);

throw err;
}
}

_connectToSocket(response) {
const url = response.webSocketDebuggerUrl;
this._pageId = response.id;

return new Promise((resolve, reject) => {
const ws = new WebSocket(url, {
perMessageDeflate: false,
});
ws.on('open', () => {
this._ws = ws;
resolve();
});
ws.on('message', data => this.handleRawMessage(/** @type {string} */ (data)));
ws.on('close', this.dispose.bind(this));
ws.on('error', reject);
});
}



static async setupDriver(driver, options) {
//...
await GatherRunner.assertNoSameOriginServiceWorkerClients(session, options.requestedUrl);
// 6. Preparing target for navigation mode,通過(guò)為全局 API 或錯(cuò)誤處理啟用協(xié)議域、仿真和新文檔處理程序,準(zhǔn)備在導(dǎo)航模式下分析的目標(biāo)。
await prepare.prepareTargetForNavigationMode(driver, options.settings);
}

static async runPass(passContext) {
//7. Running defaultPass pass

const gathererResults = {};
const {driver, passConfig} = passContext;

// 8.Resetting state with about:blank 9.Navigating to about:blankGo to about:blank
// set up

await GatherRunner.loadBlank(driver, passConfig.blankPage);
// 10.Preparing target for navigation ~ 13.Preparing network conditions
const {warnings} = await prepare.prepareTargetForIndividualNavigation(
driver.defaultSession,
passContext.settings,
{
requestor: passContext.url,
disableStorageReset: !passConfig.useThrottling,
disableThrottling: !passConfig.useThrottling,
blockedUrlPatterns: passConfig.blockedUrlPatterns,
}
);
// run `startInstrumentation() /beforePass()` on gatherers.
passContext.LighthouseRunWarnings.push(...warnings);
await GatherRunner.beforePass(passContext, gathererResults);

// 14.Beginning devtoolsLog and trace,
// await driver.beginDevtoolsLog(); await driver.beginTrace(settings);
await GatherRunner.beginRecording(passContext);
//15.Loading page & waiting for onload ,16.Navigating to https:XXX
const {navigationError: possibleNavError} = await GatherRunner.loadPage(driver, passContext);
//17.Gathering in-page: XXXXXXXX,run `pass()` on gatherers.
await GatherRunner.pass(passContext, gathererResults);
const loadData = await GatherRunner.endRecording(passContext);

//18.Gathering trace 19.Gathering devtoolsLog & network records
await emulation.clearThrottling(driver.defaultSession);

//process page error

// If no error, save devtoolsLog and trace.
GatherRunner._addLoadDataToBaseArtifacts(passContext, loadData, passConfig.passName);

// 20.Gathering XXX. Run `afterPass()(stopInstrumentation -> getArtifact )` on gatherers and return collected artifacts.
await GatherRunner.afterPass(passContext, loadData, gathererResults);
const artifacts = GatherRunner.collectArtifacts(gathererResults);


return artifacts;
}

Auditing

Audits 審查器

  • Audits是對(duì)單個(gè)功能/優(yōu)化/指標(biāo)的測(cè)試,Gatherer 采集的 artifacts 作為輸入,審查器會(huì)對(duì)其測(cè)試,然后得出相應(yīng)的測(cè)評(píng)分?jǐn)?shù)結(jié)果。
  • Computed Artifacts :根據(jù) artifacts 的需求生成,有額外的含義,經(jīng)常在在多個(gè) audits 中共享。
  • 測(cè)評(píng)結(jié)果結(jié)構(gòu):

圖片

配置

audits
  • audits 屬性控制要運(yùn)行和包含在 Lighthouse 報(bào)告中的審計(jì)。 查看更多示例以了解如何將自定義審核添加到您的配置中。
  • 具體的 audits 配置示例:
{
audits: [
'first-contentful-paint',
'byte-efficiency/uses-optimized-images',
]
}

實(shí)現(xiàn)

  1. Analyzing and running audits
  2. Auditing: XXX
  3. Generating results...
async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
//...
return Runner.audit(artifacts, options);
}
static async audit(artifacts, options) {

//...
//1. Analyzing and running audits &2.Auditing: XXX
const auditResultsById = await Runner._runAudits(settings, config.audits, artifacts,
lighthouseRunWarnings, computedCache);

//3.Generating results...
if (artifacts.LighthouseRunWarnings) {
lighthouseRunWarnings.push(...artifacts.LighthouseRunWarnings);
}

//....

}

Report 報(bào)告

客戶端根據(jù)生成 Audit 結(jié)果的 LHR.json (Lighthouse Result) 生成結(jié)果報(bào)告頁(yè)。評(píng)分報(bào)告,它包含了性能(Performance),訪問(wèn)無(wú)障礙(Accessibility),最佳實(shí)踐(Best Practice),搜索引擎優(yōu)化(SEO),PWA(Progressive Web App)5 個(gè)部分,每一項(xiàng)下面又有若干小項(xiàng)(audit),還有詳細(xì)診斷結(jié)果和優(yōu)化建議,幫助開(kāi)發(fā)者有針對(duì)性地進(jìn)行優(yōu)化。

例如:在 Lighthouse 8 中,性能得分由以下幾項(xiàng)的得分按不同的權(quán)重相加而得:

圖片

Lighthouse 8 中性能指標(biāo)權(quán)重

如何確定指標(biāo)分?jǐn)?shù)

以性能評(píng)分[7]為例,一旦 Lighthouse 收集完性能指標(biāo)(主要以毫秒為單位報(bào)告),它會(huì)通過(guò)查看指標(biāo)值在其 Lighthouse 評(píng)分分布中的位置,將每個(gè)原始指標(biāo)值轉(zhuǎn)換為從 0 到 100 的指標(biāo)分?jǐn)?shù)。評(píng)分分布是從 HTTP Archive[8] 上真實(shí)網(wǎng)站性能數(shù)據(jù)的性能指標(biāo)得出的對(duì)數(shù)正態(tài)分布。

圖片

FCP in HTTP Archive

Lighthouse 評(píng)分曲線模型使用 HTTPArchive 數(shù)據(jù)來(lái)確定兩個(gè)控制點(diǎn),然后設(shè)置對(duì)數(shù)正態(tài)曲線的形狀。HTTPArchive 數(shù)據(jù)的第 25 個(gè)百分位數(shù)變?yōu)?50 分(中值控制點(diǎn)),第 8 個(gè)百分位數(shù)變?yōu)?90 分(良好/綠色控制點(diǎn))。在探索下面的評(píng)分曲線圖時(shí),請(qǐng)注意在 0.50 和 0.92 之間,度量值和分?jǐn)?shù)之間存在近乎線性的關(guān)系。0.96 左右的分?jǐn)?shù)是上面的“收益遞減點(diǎn)”,曲線拉開(kāi),需要越來(lái)越多的指標(biāo)改進(jìn)來(lái)提高已經(jīng)很高的分?jǐn)?shù)。

圖片

探索 TTI 的評(píng)分曲線[9]

指標(biāo)得分和性能得分根據(jù)以下范圍進(jìn)行著色:

0至49(紅色):差

50至89(橙色):需要改進(jìn)

90至100(綠色):良好

為了提供良好的用戶體驗(yàn),網(wǎng)站應(yīng)該努力獲得良好的分?jǐn)?shù)(90-100)。

實(shí)現(xiàn)

static async audit(artifacts, options) {

//....
//conclusion of the lighthouse result object
const axeVersion = artifacts.Accessibility?.version;
const credits = {
'axe-core': axeVersion,
}
let categories = {};
if (config.categories) {
categories = ReportScoring.scoreAllCategories(config.categories, auditResultsById);
}
// Replace ICU message references with localized strings; save replaced paths in lhr.
i18nLhr.i18n.icuMessagePaths = format.replaceIcuMessages(i18nLhr, settings.locale);
// LHR has now been localized.
const lhr = /** @type {LH.Result} */ (i18nLhr);
if (settings.auditMode) {
const path = Runner._getDataSavePath(settings);
assetSaver.saveLhr(lhr, path);
}
// 生成報(bào)告
const report = ReportGenerator.generateReport(lhr, settings.output);
return {lhr, artifacts, report};
}

參考資料

[1]原理結(jié)構(gòu): https://github.com/GoogleChrome/lighthouse/blob/main/docs/architecture.md

[2]Puppeteer: https://github.com/puppeteer/puppeteer

[3]WebSocket: https://github.com/websockets/ws

[4]Better debugging of the Protocol: https://github.com/GoogleChrome/lighthouse/issues/184

[5]DevTools Protocol: https://chromedevtools.github.io/devtools-protocol/

[6]trace event: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview

[7]性能評(píng)分: https://web.dev/performance-scoring/

[8]HTTP Archive: https://httparchive.org/reports/state-of-the-web

[9]探索 TTI 的評(píng)分曲線: https://www.desmos.com/calculator/o98tbeyt1t

責(zé)任編輯:武曉燕 來(lái)源: ELab團(tuán)隊(duì)
相關(guān)推薦

2009-06-05 10:26:05

struts工作流程

2010-09-27 10:19:09

DHCP工作流程

2011-03-31 10:54:01

Cacti工作流程

2010-07-13 16:21:22

FIX協(xié)議

2010-06-24 16:40:16

Bittorrent協(xié)

2010-07-28 17:19:28

ICMP協(xié)議

2010-06-12 17:44:19

ARP協(xié)議

2009-07-27 14:13:15

2011-03-29 09:30:12

Cacti

2010-06-23 14:46:54

DHCP協(xié)議

2010-08-30 09:07:12

DHCP工作流程

2009-08-07 11:10:40

Netbeans ID

2023-06-05 08:14:17

RabbitMQ兔子MQ開(kāi)源

2011-08-08 15:14:11

PPPOE

2012-02-01 14:02:00

蘋果產(chǎn)品開(kāi)發(fā)

2021-10-30 18:56:12

Spring工作框架

2010-09-01 15:27:40

DHCP工作流程

2010-08-23 10:00:49

DHCP服務(wù)器

2020-10-13 21:25:15

DevOps核心

2010-07-26 14:55:56

Telnet服務(wù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)