熟悉React的同窗,可能对React Developer Tools并不陌生,
javascript
刚看到的时候,我也以为很神奇,
由于React Developer Tools和其余Chrome Extension不一样,
它竟然出如今了Chrome开发者工具栏中,和原生的DevTools同样强大。
例如,能够审查元素,查看元素的属性,等等。css
后来才知道,像这种出如今Chrome开发者工具栏中的扩展,称为Chrome DevTools Extension。html
比起普通的Chrome Extension,Chrome DevTools Extension能够访问更多API,例如,
(1)devtools.inspectedWindow
(2)devtools.network
(3)devtools.panels
其中包括了,与当前审查窗口相关的,与网络请求相关的,以及与开发者工具栏相关的API。java
在某一具体项目中,有一个这样的需求,
咱们须要选择页面中发起的http请求,而后将它们保存到数据库中。react
因为页面发起的请求可能会发往不一样的服务器,因此在服务器端解决这个问题就显得比较麻烦,
而编写一个Chrome DevTools Extension会更简单直接。git
下文我将这个功能的核心抽离出来,做为一个例子,来还原Chrome DevTools Extension的编写方法。
为此,咱们先熟悉几个基本的概念。github
Chrome浏览器是由tab页组成的,一个浏览器实例中能够打开多个tab页。web
每一个tab页,均可以打开本身的开发者工具窗口,称为DevTools Window。chrome
注意,每一个tab页都有本身独立的DevTools Window,
只是切换tab页的时候,只会显示当前tab页的DevTools Window。数据库
下面咱们来建立一个Chrome DevTools Extension项目,目录结构以下,
chrome-devtools-extension-example
├── devtools.html // DevTool Page ├── devtools.js // DevTool Page中引用的js ├── manifest.json // 入口 ├── panel.html // 开发者工具栏选项卡页面 └── panel.js // 选项卡页面中引用的js
其中manifest.json
是入口,它会声明一个对用户不可见的DevTools Page。
在本例中为devtools.html
,
{
"name": "PageRecorder", "version": "1.1.0", "minimum_chrome_version": "10.0", "description": "Record all http requests in a page.", "devtools_page": "devtools.html", "manifest_version": 2 }
DevTools Page引入的js,具备访问DevTools API的能力,
包括上文提到的那些API,devtools.inspectedWindow,devtools.network,devtools.panels。
DevTools Page对用户是不可见的,若是须要在开发者工具栏中建立新的DevTool选项卡,
还须要在DevTools Page使用一下方法来建立,DevTool选项卡,官方称为Panel。
原生的Panel包括,Elements,Console,Network,等等。
// 建立一个Panel chrome.devtools.panels.create( // title 'ChromeDevToolsExtensionExample', // iconPath null, // pagePath 'panel.html' );
以上,咱们在DevTool Page中建立了一个新的Panel,名字为ChromeDevToolsExtensionExample
。
其中,panel.html
,咱们只是简单的写了一个Hello World!
。
值得注意的是,每一个Panel均可以加载本身的html,js和css,且具备和DevTools Page同样的权限。
Panel只有在第一次被激活的时候,才进行实例化,
同一个DevTools Window中的不一样Panel切换时,不会从新加载。
当前tab页刷新时,Panel也不会从新加载。
DevTools Window关闭后,Panel将被销毁。
所以,咱们要想使用Chrome DevTools Extension,就必须先打开开发者工具窗口,
而后再激活咱们新建的DevTools Panel。
上文咱们提到了,Chrome DevTools Extension能够访问devtools.network API,
如今咱们来展现使用chrome.devtools.network.onRequestFinished.addListener
来获取请求。
为此,咱们新建了一个panel.js
文件,并在panel.html
中引用它。
// Chrome DevTools Extension中不能使用console.log const log = (...args) => chrome.devtools.inspectedWindow.eval(` console.log(...${JSON.stringify(args)}); `); // 注册回调,每个http请求响应后,都触发该回调 chrome.devtools.network.onRequestFinished.addListener(async (...args) => { try { const [{ // 请求的类型,查询参数,以及url request: { method, queryString, url }, // 该方法可用于获取响应体 getContent, }] = args; log(method, queryString, url); // 将callback转为await promise // warn: content在getContent回调函数中,而不是getContent的返回值 const content = await new Promise((res, rej) => getContent(res)); log(content); } catch (err) { log(err.stack || err.toString()); } });
以上就是panel.js
的完整内容了,咱们还须要作如下几点说明,
(1)Chrome DevTools Extension中,不能直接使用console.log
,
因此本例中使用了devtools.inspectedWindow API中的chrome.devtools.inspectedWindow.eval
方法,
在当前审查的窗口中直接求值一段js代码,从而间接实现打印日志的功能。
(2)与获取http请求的method
,queryString
,url
不一样的是,
咱们须要调用getContent
方法来获取http响应体,
而且,getContent
是一个高阶的异步函数。
所谓高阶函数,指的是,它接受一个回调函数做为参数getContent(content=>{ })
,
这个回调函数的参数content
,才是对应http请求的响应体。
所谓异步,指的是,当回调函数还没触发的时候,getContent
就已经返回了。
这也致使了事件监听函数,也不得不具备异步性。
(3)因为事件监听函数是异步的,
因此,有可能在上一个onRequestFinished
事件还未处理完的状况下,
下一个onRequestFinished
的监听函数就又被触发了。
这就致使了,以上例子中,log(method, queryString, url);
和log(content);
,
多是乱序打印的。
这个问题咱们曾经讨论过,可参考:怎样按触发顺序执行异步任务。
到此为止,咱们最简版的Chrome DevTools Extension示例已经介绍完了,
如下是能够运行的源码地址:github: chrome-extension-example
(注:不是master
分支,而是simply
分支。
Chrome扩展遵循一种优秀的设计原则,
那就是在设计系统的时候,应该想办法让扩展对用户而言,与原生功能平权。 这种对称性,会拉近原生与扩展之间的距离,从而让系统架构从一开始就创建在灵活的基础之上。