簡介
chrome-remote-interface是chrome調(diào)試協(xié)議的第三方調(diào)試客戶端實(shí)現(xiàn),該項(xiàng)目開源,提供了命令行工具,且為node程序提供了api。
chrome-remote-interface為基于chrome調(diào)試協(xié)議編寫自己的node調(diào)試工具提供了便捷的途徑,因?yàn)槔盟?,你不需要基于原始的協(xié)議通過websocket編程去開發(fā)調(diào)試工具了。
項(xiàng)目地址https://github.com/cyrus-and/chrome-remote-interface。
使用命令行
安裝
通過npm進(jìn)行安裝
npm install chrome-remote-interface
啟動(dòng)調(diào)試目標(biāo)
chrome-remote-interface基于chrome調(diào)試協(xié)議,因此其支持調(diào)試chrome瀏覽器和node運(yùn)行環(huán)境。
無論哪種調(diào)試目標(biāo),其啟動(dòng)時(shí)都應(yīng)該指定調(diào)試端口。
如果要調(diào)試chrome瀏覽器,應(yīng)該在啟動(dòng)chrome時(shí)添加--remote-debugging-port參數(shù),如下:
goole-chrome --remote-debugging-port=9222
如果調(diào)試node,在啟動(dòng)時(shí)添加--inspect參數(shù),如下:
node --inspect=9222 app.js
此時(shí),node會(huì)輸出:
Debugger listening on port 9222.
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef
題外話:如果只是希望調(diào)試node,并不打算開發(fā)一個(gè)調(diào)試node的工具的話,根據(jù)提示中的url,在chrome中打開就直接可以用chrome的開發(fā)工具調(diào)試了。
體驗(yàn)命令行
-
查看全部命令
在終端輸入chrome-remote-interface并回車,可看到如下輸出:
Usage: chrome-remote-interface [options] [command]
Commands:
inspect [options] [<target>] inspect a target (defaults to the current tab)
list list all the available tabs
new [<url>] create a new tab
activate <id> activate a tab by id
close <id> close a tab by id
version show the browser version
protocol [options] show the currently available protocol descriptor
Options:
-h, --help output usage information
-t, --host <host> HTTP frontend host
-p, --port <port> HTTP frontend port
```
其中,new和close是針對(duì)瀏覽器的tab的命令,不要針對(duì)node來運(yùn)行。
-
查看所有頁面實(shí)例
chrome-remote-interface -t 127.0.0.1 -p 9222 list
輸出如下:
[ { description: 'node.js instance', devtoolsFrontendUrl: 'https://chrome-devtools-frontend.appspot.com/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef', faviconUrl: 'https://nodejs.org/static/favicon.ico', id: '2894362d-f2d1-4f3b-a9e8-4e27da5714ef', title: 'app.js', type: 'node', url: 'file:///Users/renbaogang/git/enzyme.node/app.js', webSocketDebuggerUrl: 'ws://localhost:9222/2894362d-f2d1-4f3b-a9e8-4e27da5714ef' } ]其中devtoolsFrontendUrl可以在chrome地址欄中回車該url,打開chrome的開發(fā)工具,可以直接用該工具調(diào)試。和啟動(dòng)node時(shí),node提示的url是一個(gè)用途。
id是當(dāng)前頁面標(biāo)識(shí)。
url是當(dāng)前頁面url。
webSocketDebuggerUrl是node提供的ws協(xié)議的調(diào)試服務(wù),調(diào)試客戶端需要通過該url連接到調(diào)試服務(wù)。 -
查看調(diào)試目標(biāo)版本
chrome-remote-interface -t 127.0.0.1 -p 9222 version
輸出:
[ { Browser: 'node.js/v7.0.0', 'Protocol-Version': '1.1' } ] -
查看調(diào)試目標(biāo)支持的調(diào)試協(xié)議
chrome-remote-interface -t 127.0.0.1 -p 9222 protocol
輸出:
{ remote: false, descriptor: { { version: { major: '1', minor: '2' }, domains:[略] } }remote:false說明協(xié)議內(nèi)容是工具自帶的協(xié)議文件,并不是來自調(diào)試目標(biāo)。
如果要獲取調(diào)試目標(biāo)的協(xié)議內(nèi)容要添加-r選項(xiàng),如:chrome-remote-interface -t 127.0.0.1 -p 9222 protocol -r
輸出:
{ remote: true, descriptor: { { version: { major: '1', minor: '1' }, domains:[略] } } -
調(diào)試命令
chrome-remote-interface -t 127.0.0.1 -p 9222 inspect -r
-r 表示應(yīng)用調(diào)試目標(biāo)提供的協(xié)議描述文件
通過inspect可以調(diào)試node或chrome所支持的各個(gè)域。Console域體驗(yàn)
Console.enable()
{ result: {} }
Console.clearMessages()
{ result: {} }
```
Debugger域體驗(yàn)
```
Debugger.enable()
{ result: {} }
Debugger.setBreakpointsActive({active:true})
{ result: {} }
```
HeapProfiler域體驗(yàn)
```
HeapProfiler.enable()
{ result: {} }
HeapProfiler.startTrackingHeapObjects({trackAllocations:true})
{ result: {} }
```
Profiler域體驗(yàn)
```
Profiler.enable()
{ result: {} }
Profiler.start()
{ result: {} }
Profiler.stop()
{ result:
{ profile:
{ nodes:
[ { id: 1,
callFrame:
{ functionName: '(root)',
scriptId: '0',
url: '',
lineNumber: -1,
columnNumber: -1 },
hitCount: 0,
children: [ 2 ] },
...
]
}
}
}
```
Runtime域體驗(yàn)
>>> Runtime.evaluate({expression:"1+1"})
{ result: { result: { type: 'number', value: 2, description: '2' } } }
Schema域體驗(yàn)
>>> Schema.getDomains()
{ result:
{ domains:
[ { name: 'Runtime', version: '1.1' },
{ name: 'Debugger', version: '1.1' },
{ name: 'Profiler', version: '1.1' },
{ name: 'HeapProfiler', version: '1.1' },
{ name: 'Schema', version: '1.1' } ] } }
```
體驗(yàn)事件處理
scriptParsed是Debugger域提供的腳本解析事件
```
>>> Debugger.scriptParsed(params=>params.url)
{ 'Debugger.scriptParsed': 'params=>params.url' }
{ 'Debugger.scriptParsed': '' }
{ 'Debugger.scriptParsed': '/Users/renbaogang/git/enzyme.node/node_modules/negotiator/lib/encoding.js' }
```
## API ##
1. module([options], [callback])
基于chrome調(diào)試協(xié)議連接調(diào)試目標(biāo)
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- chooseTab 決定調(diào)試哪個(gè)tab。該參數(shù)可以為三種類型:
- function 提供一個(gè)返回tab序號(hào)的函數(shù)
- object 正如new 和 list返回的對(duì)象一樣
- string websocket url
默認(rèn)為一個(gè)函數(shù),返回當(dāng)前激活狀態(tài)的tab的序號(hào),如:function(tabs){return 0;}
- protocol 協(xié)議描述符,默認(rèn)由remote選項(xiàng)來定是否使用調(diào)試目標(biāo)提供的協(xié)議描述符
- remote 默認(rèn)false,如果protocol設(shè)置了,該選項(xiàng)不會(huì)起作用
callback
如果callback是函數(shù)類型,則該函數(shù)在connect事件發(fā)生后會(huì)得到回調(diào),并返回EventEmitter對(duì)象,如果不是,則返回Promise對(duì)象。
EventEmitter對(duì)象支持如下事件:
- connect
```
function (chrome) {}
```
當(dāng)websocket連接建立后觸發(fā),chrome是Chome類型的實(shí)例。
- disconnect
```
function () {}
```
關(guān)閉websocket連接時(shí)觸發(fā)
- error
```
function (err) {}
```
當(dāng)通過host:port/json無法到達(dá),或websocket連接無法建立時(shí)觸發(fā)
err,Error類型的錯(cuò)誤對(duì)象
使用樣例,針對(duì)chrome瀏覽器:
```
const Chrome = require('chrome-remote-interface');
Chrome(function (chrome) {
with (chrome) {
Network.requestWillBeSent(function (params) {
console.log(params.request.url);
});
Page.loadEventFired(function () {
close();
});
Network.enable();
Page.enable();
once('ready', function () {
Page.navigate({'url': 'https://github.com'});
});
}
}).on('error', function (err) {
console.error('Cannot connect to Chrome:', err);
});
```
2. module.Protocol([options],[callback])
獲取chrome調(diào)試協(xié)議描述符
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- remote 默認(rèn)false
callback 回調(diào)函數(shù),在獲取到協(xié)議內(nèi)容后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
- protocol 包含以下屬性
- remote 是否遠(yuǎn)程協(xié)議
- descriptor chrome調(diào)試協(xié)議描述符
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.Protocol(function (err, protocol) {
if (!err) {
console.log(JSON.stringify(protocol.descriptor, null, 4));
}
});
```
3. module.List([options], [callback])
獲取所有的tabs,當(dāng)然在node中只會(huì)有一個(gè)
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- remote 默認(rèn)false
callback 回調(diào)函數(shù),在獲取到tabs內(nèi)容后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
- tabs 獲取到的tab數(shù)組
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.List(function (err, tabs) {
if (!err) {
console.log(tabs);
}
});
```
4. module.New([options], [callback])
創(chuàng)建新的tab
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- url 新tab加載的url 默認(rèn)about:blank
callback 回調(diào)函數(shù),在新tab創(chuàng)建后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
- tab 新增的tab
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.New(function (err, tab) {
if (!err) {
console.log(tab);
}
});
-
module.Activate([options], [callback])
激活指定tab
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- id 目標(biāo)tab的id
callback 回調(diào)函數(shù),在新tab創(chuàng)建后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.Activate({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is closing');
}
});
```
-
module.Close([options], [callback])
關(guān)閉指定tab
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
- id 目標(biāo)tab的id
callback 回調(diào)函數(shù),在新tab創(chuàng)建后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.Close({'id': 'CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC'}, function (err) {
if (!err) {
console.log('success! tab is closing');
}
});
```
-
module.Version([options], [callback])
獲取版本信息
options object類型,具有如下屬性:
- host 默認(rèn)localhost
- port 默認(rèn)9222
callback 回調(diào)函數(shù),在新tab創(chuàng)建后調(diào)用,函數(shù)接收如下參數(shù):
- err Error一個(gè)錯(cuò)誤對(duì)象,如果有錯(cuò)誤的話
- info json object
使用樣例:
```
const Chrome = require('chrome-remote-interface');
Chrome.Version(function (err, info) {
if (!err) {
console.log(info);
}
});
```
-
Chrome 類
支持的事件:
-
event
在遠(yuǎn)程調(diào)試目標(biāo)發(fā)送通知時(shí)觸發(fā),一般是遠(yuǎn)程對(duì)象執(zhí)行了客戶端提交的方法后
function (message) {}message包含如下屬性:
- method 通知內(nèi)容,方法名 如:'Network.requestWillBeSent'
- params 參數(shù)內(nèi)容
使用樣例:
``` chrome.on('event', function (message) { if (message.method === 'Network.requestWillBeSent') { console.log(message.params); } }); ```-
<method>
調(diào)試目標(biāo)通過websocket發(fā)送了一個(gè)指定方法名的通知
function (params) {}使用樣例:
chrome.on('Network.requestWillBeSent', console.log); -
ready
每次沒有調(diào)試命令等待調(diào)試目標(biāo)返回時(shí)觸發(fā)
function () {}使用樣例:
只在Network和Page激活后加載一個(gè)url
chrome.Network.enable(); chrome.Page.enable(); chrome.once('ready', function () { chrome.Page.navigate({'url': 'https://github.com'}); });
-
支持的方法
- chrome.send(method, [params], [callback])
發(fā)送一個(gè)調(diào)試命令
method 命令名稱
params 參數(shù)
callback 當(dāng)遠(yuǎn)程對(duì)象對(duì)該命令發(fā)送一個(gè)應(yīng)答后調(diào)用,函數(shù)具有以下參數(shù):
- error boolean 是否成功
- response 如果error===true,返回一個(gè)error對(duì)象,{error:...},否則返回一個(gè)應(yīng)答,{result:...}
注意:在chrome調(diào)試規(guī)范里提到的id字段,在這里被內(nèi)部管理不會(huì)暴露給用戶
使用樣例:
```
chrome.send('Page.navigate', {'url': 'https://github.com'}, console.log);
```
- chrome.<domain>.<method>([params], [callback])
是chrome.send('<domain>.<method>', params, callback);的一種變形
例如:
```
chrome.Page.navigate({'url': 'https://github.com'}, console.log);
```
- chrome.<domain>.<event>(callback)
是chrome.on('<domain>.<event>', callback)的變形
例如:
```
chrome.Network.requestWillBeSent(console.log);
```
- chrome.close([callback])
關(guān)閉與遠(yuǎn)程調(diào)試目標(biāo)的連接
callback會(huì)在websocket關(guān)閉成功后調(diào)用