熟悉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擴展遵循一種優秀的設計原則,
那就是在設計系統的時候,應該想辦法讓擴展對用戶而言,與原生功能平權。 這種對稱性,會拉近原生與擴展之間的距離,從而讓系統架構從一開始就創建在靈活的基礎之上。