黃聰:如何擴展Chrome DevTools來獲取頁面請求


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
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM