1. Chrome DevTools Extension

熟悉React的同学,可能对React Developer Tools并不陌生,

 
 

刚看到的时候,我也觉得很神奇,
因为React Developer Tools和其他Chrome Extension不同,
它居然出现在了Chrome开发者工具栏中,和原生的DevTools一样强大。
例如,可以审查元素,查看元素的属性,等等。

后来才知道,像这种出现在Chrome开发者工具栏中的扩展,称为Chrome DevTools Extension

比起普通的Chrome Extension,Chrome DevTools Extension可以访问更多API,例如,
(1)devtools.inspectedWindow
(2)devtools.network
(3)devtools.panels
其中包括了,与当前审查窗口相关的,与网络请求相关的,以及与开发者工具栏相关的API。

2. 背景 & 基本概念

 
 

在某一具体项目中,有一个这样的需求,
我们需要选择页面中发起的http请求,然后将它们保存到数据库中。

由于页面发起的请求可能会发往不同的服务器,所以在服务器端解决这个问题就显得比较麻烦,
而编写一个Chrome DevTools Extension会更简单直接。

下文我将这个功能的核心抽离出来,作为一个例子,来还原Chrome DevTools Extension的编写方法。
为此,我们先熟悉几个基本的概念。

(1)tab页

Chrome浏览器是由tab页组成的,一个浏览器实例中可以打开多个tab页。

 
 

(2)DevTools Window

每个tab页,都可以打开自己的开发者工具窗口,称为DevTools Window。

 
 

注意,每个tab页都有自己独立的DevTools Window,
只是切换tab页的时候,只会显示当前tab页的DevTools Window。

(3)DevTools Page 和 Panel

下面我们来创建一个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.inspectedWindowdevtools.networkdevtools.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一样的权限。

(4)Panel的生命周期

Panel只有在第一次被激活的时候,才进行实例化,
同一个DevTools Window中的不同Panel切换时,不会重新加载。
当前tab页刷新时,Panel也不会重新加载。

DevTools Window关闭后,Panel将被销毁。

因此,我们要想使用Chrome DevTools Extension,就必须先打开开发者工具窗口,
然后再激活我们新建的DevTools Panel。

3. 监听请求

 
 

上文我们提到了,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请求的methodqueryStringurl不同的是,
我们需要调用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扩展遵循一种优秀的设计原则,
那就是在设计系统的时候,应该想办法让扩展对用户而言,与原生功能平权
这种对称性,会拉近原生与扩展之间的距离,从而让系统架构从一开始就建立在灵活的基础之上。

作者:何幻
链接:https://www.jianshu.com/p/4ce7f58b8c84
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

黄聪:如何扩展Chrome DevTools来获取页面请求的更多相关文章

  1. Spring MVC(三)控制器获取页面请求参数以及将控制器数据传递给页面和实现重定向的方式

    首先做好环境配置 在mvc.xml里进行配置 1.开启组件扫描 2.开启基于mvc的标注 3.配置试图处理器 <?xml version="1.0" encoding=&qu ...

  2. Struts2------Result处理&获取页面请求参数&API

    一.Result处理 1.1 说明 平常我们设置跳转页面,是在action标签里面加上 result标签来控制,这种设置的页面跳转,称之为 局部结果页面:但是我们有时候在很多个action里面,针对不 ...

  3. 黄聪:jquery mobile通过a标签页面跳转后,样式丢失、js失效的解决方法

    问题描述: 用ajax跳转的时候,从a.html跳转到b.html后,b.html的css以及js都失效了. 解决办法1: 将所有的css以及js全部放在div内. 原理: 由于jqm的ajax跳转的 ...

  4. 黄聪:Pjax无刷新跳转页面实现,支持超链接与表单提交

    什么是pjax? 当你点击一个站内的链接的时候,不是做页面跳转,而是只是站内页面刷新.这样的用户体验,比起整个页面都闪一下来说, 好很多. 其中有一个很重要的组成部分, 这些网站的ajax刷新是支持浏 ...

  5. 黄聪:如何使用Add-on SDK开发一个自己的火狐扩展

    火狐开放了扩展的开发权限给程序员们,相信很多人都会希望自己做一些扩展来方便一些使用. 我最近做一些项目也需要开发一个火狐扩展,方便收集自己需要的数据,因此研究了几天怎么开发,现在已经差不多完成了,就顺 ...

  6. Chrome扩展之css used 获取网页样式

    地址栏输入: chrome://extensions/ 然后获取更多扩展程序,得到css used 复制html节点 最后点击 "css used" 把样式全部复制下来即可 (记住 ...

  7. Chrome DevTools 面板全攻略

    李华西,微医云服务团队前端开发工程师,喜欢瞎折腾,典型猫奴 Console 面板 此章节请打开 devtools/console/console.html 一起食用 一方面用来记录页面在执行过程中的信 ...

  8. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (初级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (初级) 企业库加密应用程序模块提供了2种方 ...

  9. 黄聪:Microsoft Enterprise Library 5.0 系列教程(一) : Caching Application Block (初级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(一) : Caching Application Block (初级) 本篇文章具体官方解释请参照以下链接: h ...

随机推荐

  1. CentOS 7上安装Pure-FTPd

    # 安装 yum install epel-release yum install pure-ftpd 位置文件位于/etc/pure-ftpd/pure-ftpd.conf # 修改配置文件 # 注 ...

  2. Python—全局变量、局部变量、匿名函数

    局部变量和全局变量 college1 = 'JMU' #全局变量 def change_name(name): college1 = 'LiGong' #局部变量,此函数是其作用域 # global ...

  3. Centos7 systemctl和防火墙firewalld命令(参考https://www.cnblogs.com/marso/archive/2018/01/06/8214927.html)

    一.防火墙的开启.关闭.禁用命令 (1)设置开机启用防火墙:systemctl enable firewalld.service (2)设置开机禁用防火墙:systemctl disable fire ...

  4. xenserver挂载新硬盘

    注意:新加硬盘请不要加入raid,否则不认盘 一: 1.1:查看磁盘列表 fdisk -l [root@xenserver zz]# fdisk -l Disk /dev/sdb: 7999.4 GB ...

  5. Caffe 根据log信息画出loss,accuracy曲线

    在执行训练的过程中,若指定了生成log信息,log信息包含初始化,网络结构初始化和训练过程随着迭代数的loss信息. 注意生成的log文件可能没有.log后缀,那么自己加上.log后缀.如我的log信 ...

  6. 补码的来源以及为什么byte的最小值是-128

    1.        有符号数和无符号数 我们的实数分为正数和负数和0三部分 Byte数据类型一共有8位,如果是无符号数,最大可以表示的数为11111111 = 256 -1 = 255 无符号数代指不 ...

  7. re模块+面向对象

    re模块 re:其实就是带有特殊语法的字符串 语法:单个字符和多个字符 单个字符: \d是匹配所有的数字 \D是匹配所有的非数字 \s是所有的换行符,制表符,空白等,回车符 \S是所有费换行符,空白和 ...

  8. C++ 常用设计模式(学习笔记)

    1.工厂模式:简单工厂模式.工厂方法模式.抽象工厂模式 1).简单工厂模式:主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类. typedef enum { T80 ...

  9. java学习笔记37(sql工具类:JDBCUtils)

    在之前的内容中,我们发现,当我们执行一条语句时,每新建一个方法,就要重新连接一次数据库,代码重复率很高,那么能不能把这些重复代码封装成一个类呢,我们学习方法时,就学习到方法就是为了提高代码的利用率,所 ...

  10. WEBBASE篇: 第八篇, JavaScript知识2

    JavaScript 2 一,数据类型: 作用: 约束了数据在内存中所占空间大小问题的: JS数据类型分类: 基本数据类型(值类型) (1) number 类型: 数字类型,可以表示32位的整数或64 ...