日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
自己實現(xiàn)ChromeDevTools的Coverage功能

Chrome DevTools 有一個覆蓋率檢測的功能,可以檢測 JS、CSS 代碼里有哪些執(zhí)行了,哪些沒執(zhí)行。并且還會在 sources 里標(biāo)記出來。

創(chuàng)新互聯(lián)建站總部坐落于成都市區(qū),致力網(wǎng)站建設(shè)服務(wù)有成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計、網(wǎng)絡(luò)營銷策劃、網(wǎng)頁設(shè)計、網(wǎng)站維護、公眾號搭建、小程序開發(fā)、軟件開發(fā)等為企業(yè)提供一整套的信息化建設(shè)解決方案。創(chuàng)造真正意義上的網(wǎng)站建設(shè),為互聯(lián)網(wǎng)品牌在互動行銷領(lǐng)域創(chuàng)造價值而不懈努力!

如下圖,綠色的部分是執(zhí)行過的,而紅色的部分是沒執(zhí)行的:

在 sources 面板里可以直接看到哪些代碼沒執(zhí)行,比如下面的紅色部分就是沒有執(zhí)行的:

這個功能還是很有用的,可以幫助我們分析哪些代碼是用不到的,可以進行延后加載或者刪掉等優(yōu)化。

在 More Tools 里開啟:

使用還是很簡單的,但它是怎么實現(xiàn)的呢?

代碼是否運行過的數(shù)據(jù)只有運行時才能得到,所以肯定是 Chrome 暴露出來,傳給 Chrome DevTools 做分析和展示的。

Chrome 和 Chrome DevTools 的通信是通過 CDP(Chrome DevTools Protocol)協(xié)議。

傳輸協(xié)議數(shù)據(jù)有多種信道,遠程調(diào)試的時候是通過 WebSocket,嵌入的時候就直接通過全局變量了。

Chrome 啟動的時候,可以通過 --remote-debugging-port 指定 ws 服務(wù)的端口:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222

我們自己實現(xiàn)一個 ws 客戶端連上它,就能拿到所有的 CDP 數(shù)據(jù)。

那我們是否能自己實現(xiàn)一下 JS、CSS 的覆蓋率檢測功能呢?

肯定是可以的。

自己實現(xiàn) ws 客戶端,傳輸 CDP 協(xié)議數(shù)據(jù)這部分可以用 google 提供的一個包 chrome-remote-interface。

const CDP = require('chrome-remote-interface');
async function test() {
let client;
try {
client = await CDP({
host: '127.0.0.1',
port: 9222
});
const { Page, DOM, Debugger, Runtime, CSS, Profiler } = client;


} catch(err) {
console.error(err);
}
}
test();

連接上 9229 端口,通過各個域的 api 進行 CDP 的交互即可。

CDP 協(xié)議分為了很多個域來管理,比如 DOM、CSS、Debugger 等:

可以通過 Chrome DevTools 的 Protocol Monitor 來查看傳輸?shù)膮f(xié)議數(shù)據(jù):

數(shù)據(jù)交互分為兩類,一類是服務(wù)端推送過來的事件,另一類是向服務(wù)端請求的數(shù)據(jù)。

CDP 介紹完了,接下來我們實現(xiàn)下覆蓋率檢測的功能。

首先,我們要知道頁面下載了哪些 JS 和 CSS。

這個是通過監(jiān)聽事件拿到的, CSS.styleSheetAdded 和 Debugger.scriptParsed 這倆事件。

我們監(jiān)聽下這倆事件:

const CDP = require('chrome-remote-interface');
async function test() {
let client;
try {
client = await CDP({
host: '127.0.0.1',
port: 9222
});
const { Page, DOM, Debugger, Runtime, CSS } = client;

await Page.enable();
await Debugger.enable();
await DOM.enable();
await CSS.enable();

CSS.on('styleSheetAdded', async (event) => {
debugger;
})
Debugger.on('scriptParsed', async (event) => {
debugger;
})
await Page.navigate({url: 'http://127.0.0.1:8084'});

} catch(err) {
console.error(err);
}
}
test();

因為用到 DOM、CSS、Debugger、Page 域的協(xié)議,所以需要先 enable 一下,只有 enable的功能才會啟用。

這個很正常,沒 enable 就不啟用,這樣能節(jié)省性能。

執(zhí)行這段代碼,看下拿到的事件對象:

事件對象里是這段 js 的 url 和行列號,再就是 scriptId。

然后再看下 CSS.styleSheetAdded 的事件對象:

也差不多,只不過這里是 styleSheetId。

那怎么拿到 CSS 和 JS 的內(nèi)容呢?

這就需要用到別的 api 了。

css 的內(nèi)容是用 CSS.getStyleSheetText 來拿,傳入 styeleSheetId:

const styleSheetId = event.header.styleSheetId;
const content = await CSS.getStyleSheetText({ styleSheetId });

JS 的內(nèi)容是用 Debugger.getScriptSource 來拿,傳入 scriptId:

const scriptId = event.scriptId;
const content = await Debugger.getScriptSource({ scriptId });

我們把它們按照 id 放到 Map 里:

const cssMap = new Map();
const jsMap = new Map();
CSS.on('styleSheetAdded', async (event) => {
const styleSheetId = event.header.styleSheetId;
const content = await CSS.getStyleSheetText({ styleSheetId });
cssMap.set(styleSheetId, {
meta: event.header,
content: content.text
});
})
Debugger.on('scriptParsed', async (event) => {
const scriptId = event.scriptId;
const content = await Debugger.getScriptSource({ scriptId });

jsMap.set(scriptId, {
meta: event,
content: content.scriptSource
});
})

這樣就能把頁面上所有的 js 和 css 收集起來:

對了,測試頁面的內(nèi)容是這樣的:







Document







有一個外部 css:

.aaa {
color: red;
}
div {
color: blue;
}
body {
background: pink;
}

收集到了 JS 和 CSS 的數(shù)據(jù)只是第一步,要計算出覆蓋率數(shù)據(jù),還要知道哪些 JS 和 CSS 執(zhí)行了。

這個也有 api:

CSS 開啟執(zhí)行數(shù)據(jù)的收集是用 CSS.startRuleUsageTracking:

await CSS.enable();
await CSS.startRuleUsageTracking();

然后一段時間后 stop:

// 延遲一段時間再獲取數(shù)據(jù),等頁面渲染完
await new Promise(resolve => setTimeout(resolve, 3000));
const cssCoverage = await CSS.stopRuleUsageTracking();

這樣就能獲取 CSS 的執(zhí)行數(shù)據(jù):

返回的結(jié)果顯示 scriptId 為 89607.4 的 css 的 50 到 80 個字符的代碼執(zhí)行了。

我們在 cssMap 里看下這個 id 對應(yīng)的代碼:

然后取出 50 到 80 個字符的代碼:

也就是說所有 css 里只有這一段代碼是生效的:

你用 Chrome DevTools 的 Coverage 分析結(jié)果也是這樣的:

有了所有 CSS 代碼的數(shù)據(jù),有了執(zhí)行了哪些 CSS 的代碼的數(shù)據(jù),覆蓋率的計算不就很簡單了么?

我們再來看下 JS 的:

JS 使用 Profiver 的 prociseCoverage 的 api 獲取覆蓋率數(shù)據(jù):

await Profiler.enable();
await Profiler.startPreciseCoverage();
// 延遲一會再獲取數(shù)據(jù),等 js 執(zhí)行完
await new Promise(resolve => setTimeout(resolve, 3000));
const jsCoverage = await Profiler.takePreciseCoverage();

可以看到返回了兩個 script 的執(zhí)行數(shù)據(jù):

因為我們頁面上就兩個 script 嘛:

第一個 script 有 4 個 functions:

有同學(xué)說,不對呀,不是 add、minus、multiply 3 個嗎?

那個沒有名字的代表 script 的匿名代碼塊。

每個 function 都記錄了字符的范圍,還有執(zhí)行的次數(shù):

比如 add 函數(shù)執(zhí)行了 1 次:

minus 函數(shù)執(zhí)行了 0 次:

第二個 script 的匿名代碼塊執(zhí)行了 1 次:

這不就和 Chrome DevTools 的 Coverage 結(jié)果對上了么:

不管是覆蓋率數(shù)據(jù)也好,還是在 sources 里可視化展示哪些代碼沒執(zhí)行也好,都很容易實現(xiàn)。

這部分的全部代碼如下,感興趣的同學(xué)可以試試:

const CDP = require('chrome-remote-interface');
async function test() {
let client;
try {
client = await CDP({
host: '127.0.0.1',
port: 9222
});
const { Page, DOM, Debugger, Runtime, CSS, Profiler } = client;
await Page.enable();
await Debugger.enable();
await DOM.enable();
await CSS.enable();
await Profiler.enable();
const cssMap = new Map();
const jsMap = new Map();
CSS.on('styleSheetAdded', async (event) => {
const styleSheetId = event.header.styleSheetId;
const content = await CSS.getStyleSheetText({ styleSheetId });
cssMap.set(styleSheetId, {
meta: event.header,
content: content.text
});
})
Debugger.on('scriptParsed', async (event) => {
const scriptId = event.scriptId;
const content = await Debugger.getScriptSource({ scriptId });

jsMap.set(scriptId, {
meta: event,
content: content.scriptSource
});
})
await CSS.startRuleUsageTracking();
await Profiler.startPreciseCoverage();

await Page.navigate({url: 'http://127.0.0.1:8084'});

await new Promise(resolve => setTimeout(resolve, 3000));

const cssCoverage = await CSS.stopRuleUsageTracking();
const jsCoverage = await Profiler.takePreciseCoverage();
debugger;
} catch(err) {
console.error(err);
}
}
test();

有的同學(xué)可能問了,Chrome DevTools 會用不就行了么,我管它怎么實現(xiàn)的干嘛?

確實,大多數(shù)業(yè)務(wù)開發(fā)同學(xué)會用 Chrome DevTools 就行了,但是如果你要實現(xiàn)一個調(diào)試工具呢?那就要深入理解它的原理了。而且理解了原理,你再去用也更加得心應(yīng)手。

更重要的是,通過 api 的方式,你是能拿到運行時的數(shù)據(jù)的,可以自己做一些計算和處理,然后把數(shù)據(jù)存下來之類的。

不知道大家有沒有聽說過 lighthouse,就是分析頁面性能、可訪問性等等數(shù)據(jù),然后給出一個得分和優(yōu)化建議的工具:

它其實是有獨立的 cli 的:

在 cli 里怎么收集網(wǎng)頁的數(shù)據(jù),然后做分析呢?

其實它就是通過 Chrome 運行網(wǎng)頁,然后 CDP 的方式收集各種數(shù)據(jù),然后做分析和展示的。

如果某一天,你也要做一個網(wǎng)頁分析工具,是不是也可以通過 CDP 的方式來獲取一些網(wǎng)頁運行數(shù)據(jù)做分析呢?

所有 Chrome DevTools 的數(shù)據(jù),你通過 CDP 都是能拿到的,能做的事情有很多。

總結(jié)

Chrome DevTools 有 Coverage 面板,可以分析 JS 和 CSS 代碼執(zhí)行的覆蓋率,分析出哪些代碼沒執(zhí)行,然后做后續(xù)優(yōu)化。

這是 Chrome 通過 CDP 暴露給 Chrome DevTools 的,而 CDP 的數(shù)據(jù)我們也能自己實現(xiàn) ws 客戶端來拿到,那自然也可以自己實現(xiàn)覆蓋率的計算。

我們通過 chrome-remote-interface 的不同域的 api 來進行了 CSS 和 JS 的代碼的收集,代碼執(zhí)行數(shù)據(jù)的收集,有了這些數(shù)據(jù)就能輕松算出覆蓋率。

lighthouse 的 cli 就是通過這種方式來收集 Chrome 運行時數(shù)據(jù),做分析和展示的。如果我們想做一個調(diào)試工具,或者網(wǎng)頁分析工具,也可以用類似的思路。

Chrome DevTools 能做的所有事情,我們都能自己實現(xiàn),因為 CDP 數(shù)據(jù)是一摸一樣的。

你還對啥 Chrome DevTools 的功能感興趣呢?不如我們自己來實現(xiàn)一下?


當(dāng)前名稱:自己實現(xiàn)ChromeDevTools的Coverage功能
網(wǎng)頁鏈接:http://www.dlmjj.cn/article/ccieche.html