自己实现 Chrome DevTools 的 Coverage 功能

[IT科技类资讯] 时间:2025-11-05 11:17:16 来源:益强IT技术网 作者:IT科技类资讯 点击:114次

Chrome DevTools 有一个覆盖率检测的自己功能,可以检测 JS、实现CSS 代码里有哪些执行了,自己哪些没执行。实现并且还会在 sources 里标记出来。自己

如下图,实现绿色的自己部分是执行过的,而红色的实现部分是没执行的:

在 sources 面板里可以直接看到哪些代码没执行,比如下面的自己红色部分就是没有执行的:

这个功能还是很有用的,可以帮助我们分析哪些代码是实现用不到的,可以进行延后加载或者删掉等优化。自己

在 More Tools 里开启:

使用还是实现很简单的,但它是自己怎么实现的呢?

代码是否运行过的数据只有运行时才能得到,所以肯定是亿华云实现 Chrome 暴露出来,传给 Chrome DevTools 做分析和展示的自己。

Chrome 和 Chrome DevTools 的通信是通过 CDP(Chrome DevTools Protocol)协议。

传输协议数据有多种信道,远程调试的时候是通过 WebSocket,嵌入的时候就直接通过全局变量了。

Chrome 启动的时候,可以通过 --remote-debugging-port 指定 ws 服务的端口:

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

我们自己实现一个 ws 客户端连上它,就能拿到所有的 CDP 数据。

那我们是否能自己实现一下 JS、CSS 的覆盖率检测功能呢?

肯定是可以的。

自己实现 ws 客户端,传输 CDP 协议数据这部分可以用 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 协议分为了很多个域来管理,比如 DOM、云服务器提供商CSS、Debugger 等:

可以通过 Chrome DevTools 的 Protocol Monitor 来查看传输的协议数据:

数据交互分为两类,一类是服务端推送过来的事件,另一类是向服务端请求的数据。

CDP 介绍完了,接下来我们实现下覆盖率检测的功能。

首先,我们要知道页面下载了哪些 JS 和 CSS。

这个是通过监听事件拿到的, CSS.styleSheetAdded 和 Debugger.scriptParsed 这俩事件。

我们监听下这俩事件:

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 域的协议,所以需要先 enable 一下,只有 enable的功能才会启用。

这个很正常,没 enable 就不启用,这样能节省性能。

执行这段代码,看下拿到的事件对象:

事件对象里是免费源码下载这段 js 的 url 和行列号,再就是 scriptId。

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

也差不多,只不过这里是 styleSheetId。

那怎么拿到 CSS 和 JS 的内容呢?

这就需要用到别的 api 了。

css 的内容是用 CSS.getStyleSheetText 来拿,传入 styeleSheetId:

const styleSheetId = event.header.styleSheetId;

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

JS 的内容是用 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 收集起来:

对了,测试页面的内容是这样的:

Document

a {

color: red;

}

function add(a, b) {

return a + b;

}

function minus(a, b) {

return a -b;

}

function multiply(a, b) {

return a * b;

}

add(1, 3);

multiply(3, 4);

</html>

有一个外部 css:

.aaa {

color: red;

}

div {

color: blue;

}

body {

background: pink;

}

收集到了 JS 和 CSS 的数据只是第一步,要计算出覆盖率数据,还要知道哪些 JS 和 CSS 执行了。

这个也有 api:

CSS 开启执行数据的收集是用 CSS.startRuleUsageTracking:

await CSS.enable();

await CSS.startRuleUsageTracking();

然后一段时间后 stop:

// 延迟一段时间再获取数据,等页面渲染完

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

const cssCoverage = await CSS.stopRuleUsageTracking();

这样就能获取 CSS 的执行数据:

返回的结果显示 scriptId 为 89607.4 的 css 的 50 到 80 个字符的代码执行了。

我们在 cssMap 里看下这个 id 对应的代码:

然后取出 50 到 80 个字符的代码:

也就是说所有 css 里只有这一段代码是生效的:

你用 Chrome DevTools 的 Coverage 分析结果也是这样的:

有了所有 CSS 代码的数据,有了执行了哪些 CSS 的代码的数据,覆盖率的计算不就很简单了么?

我们再来看下 JS 的:

JS 使用 Profiver 的 prociseCoverage 的 api 获取覆盖率数据:

await Profiler.enable();

await Profiler.startPreciseCoverage();

// 延迟一会再获取数据,等 js 执行完

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

const jsCoverage = await Profiler.takePreciseCoverage();

可以看到返回了两个 script 的执行数据:

因为我们页面上就两个 script 嘛:

第一个 script 有 4 个 functions:

有同学说,不对呀,不是 add、minus、multiply 3 个吗?

那个没有名字的代表 script 的匿名代码块。

每个 function 都记录了字符的范围,还有执行的次数:

比如 add 函数执行了 1 次:

minus 函数执行了 0 次:

第二个 script 的匿名代码块执行了 1 次:

这不就和 Chrome DevTools 的 Coverage 结果对上了么:

不管是覆盖率数据也好,还是在 sources 里可视化展示哪些代码没执行也好,都很容易实现。

这部分的全部代码如下,感兴趣的同学可以试试:

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();

有的同学可能问了,Chrome DevTools 会用不就行了么,我管它怎么实现的干嘛?

确实,大多数业务开发同学会用 Chrome DevTools 就行了,但是如果你要实现一个调试工具呢?那就要深入理解它的原理了。而且理解了原理,你再去用也更加得心应手。

更重要的是,通过 api 的方式,你是能拿到运行时的数据的,可以自己做一些计算和处理,然后把数据存下来之类的。

不知道大家有没有听说过 lighthouse,就是分析页面性能、可访问性等等数据,然后给出一个得分和优化建议的工具:

它其实是有独立的 cli 的:

在 cli 里怎么收集网页的数据,然后做分析呢?

其实它就是通过 Chrome 运行网页,然后 CDP 的方式收集各种数据,然后做分析和展示的。

如果某一天,你也要做一个网页分析工具,是不是也可以通过 CDP 的方式来获取一些网页运行数据做分析呢?

所有 Chrome DevTools 的数据,你通过 CDP 都是能拿到的,能做的事情有很多。

总结

Chrome DevTools 有 Coverage 面板,可以分析 JS 和 CSS 代码执行的覆盖率,分析出哪些代码没执行,然后做后续优化。

这是 Chrome 通过 CDP 暴露给 Chrome DevTools 的,而 CDP 的数据我们也能自己实现 ws 客户端来拿到,那自然也可以自己实现覆盖率的计算。

我们通过 chrome-remote-interface 的不同域的 api 来进行了 CSS 和 JS 的代码的收集,代码执行数据的收集,有了这些数据就能轻松算出覆盖率。

lighthouse 的 cli 就是通过这种方式来收集 Chrome 运行时数据,做分析和展示的。如果我们想做一个调试工具,或者网页分析工具,也可以用类似的思路。

Chrome DevTools 能做的所有事情,我们都能自己实现,因为 CDP 数据是一摸一样的。

你还对啥 Chrome DevTools 的功能感兴趣呢?不如我们自己来实现一下?

(责任编辑:系统运维)

    联通4G手机卡的使用体验及优缺点分析(畅享高速网络,联通4G手机卡带来的便利与不足)ubuntu安装和配置SVN第一步:安装apache2  libapache2-svn subversion复制代码代码如下:复制代码代码如下:复制代码代码如下:apt-get install subversion2. 建立svn仓库1). 建立svn目录:mkdir /home/.svn(使用隐藏目录)2). cd /home/.svn    3). mkdir astar4). 创建仓库astar:svnadmin create /home/.svn/astar,执行完毕后astar目录有svnadmin创建的目录和文件5). mkdir test6). 创建仓库test:svnadmin create /home/.svn/test,执行完毕后test目录有svnadmin创建的目录和文件3. 配置和管理svn1). 每个仓库的配置文件在$repos/conf/下,vi svnserve.conf,配置项在[general]下:anon-access:匿名用户的权限,可以为read,write和none,默认值read。不允许匿名用户访问:anon-access = noneauth-access:认证用户的权限,可以为read,write和none,默认值write。password-db:密码数据库的路径,去掉前边的#authz-db:认证规则库的路径,去掉前边的#。注意:这些配置项的行都要顶格,否则会报错。修改配置后需要重启svn才能生效。2). 配置passwd文件这是每个用户的密码文件,比较简单,就是“用户名=密码”,采用的是明码。如allen=1111113). 配置authz文件1. [groups] section:为了便于管理,可以将一些用户放到一个组里边,比如:owner=allen,ellen2. groups下边的sections表示对一个目录的认证规则,比如对根目录的认证规则的section为[/]。设置单用户的认证规则时一个用户一行,如:  [/]      allen=rw  #allen对根目录的权限为rw  ellen=r    #ellen对根目录的权限为r  假如使用group,需要在group名字前加@,如  @owner=rw  #group owner中的用户均为rw,等价于上边的两句话启动时假如从/home/.svn/astar启动,/就是astar目录,用如上方式以astar目录为根设置权限。假如从/home/.svn/启动,每个仓库根还是自己的起始目录。可以采用如上方式设置astar的权限,也可以采用如下方式:  [astar:/]  @owner=rw设置test的权限如下:  [test:/]  @harry_and_sally = rw简言之,每个仓库的根目录(/)就是自己的起始目录;[repos:/]这种方式只适用于多仓库的情况;[/]适合于单仓库和单仓库的方式。3. 不能跨越仓库设置权限。4. 启动和停止svn    1). 启动:1. 从astar目录启动,svnserve -d -r /home/.svn/astar,根目录(/)是astar,authz中规则的配置使用section[/]。访问方式为:  svn://192.168.0.87/2. 从.svn目录启动,svnserve -d -r /home/.svn,根目录(/)是.svn,authz中对astar的配置使用section[astar:/] ,对test的配置使用section[test:/]。访问方式为:  svn://192.18.0.87/astar  svn://192.18.0.87/test假如需要svn自启动,把命令加入/etc/rc.local中2). 检查svn服务器是否已经启动(svn默认使用3690端口):netstat -an | grep 36903). 停止:killall svnserve5. svn client推荐使用TortoiseSVN, http://tortoisesvn.tigris.org/Eclipse插件,http://subclipse.tigris.org/作者 t80t90s
    相关内容
    精彩推荐
    热门点击
    友情链接