中对Node程序开始展览调和,编制程序式调节和测试node程序的利器chrome

报料浏览器远程调节和测试才具

2016/10/19 · 基本功手艺 ·
1 评论 ·
浏览器,
调试,
远程

原来的文章出处: Taobao前端团队(FED) –
肖焉   

美高梅开户网址 1

美高梅开户网址 2

简介

chrome-remote-interface是chrome调节和测试协议的第一方调度客户端达成,该品种开源,提供了命令行工具,且为node程序提供了api。
chrome-remote-interface为依靠chrome调节和测试协议编写本身的node调节和测试工具提供了简便易行的渠道,因为使用它,你不供给依据原始的研究通过websocket编程去付出调节和测试工具了。
品种地址https://github.com/cyrus-and/chrome-remote-interface。

启动node.js脚本

$ NODE_ENV=production API_KEY=442CC1FE-4333-46CE-80EE-6705A1896832
node server.js

调整才具的源点

1玖4七 年 玖 月 9 日,一名U.S.的物军事学家格蕾丝.霍普和她的伴儿在对 马克 II
Computer实行切磋的时候发掘,三头飞蛾粘在叁个继电器上,导致Computer无法符合规律专门的学业,当他们把飞蛾移除之后,Computer又过来了正规运转。于是他们将这只飞蛾贴在了她们及时记下的日志上,对这件业务举办了详实的笔录,并在日记最终写了这么一句话:First
actual case of bug being found。那是她们发觉的第2个真正含义上的
bug,那也是人类APP历史上,发掘的首先个
bug,而他们找到飞蛾的秘籍和经过,正是 debugging 调节和测试才干。

美高梅开户网址 3

从格蕾丝调试第壹个 bug 到近期,6九年的时光里,在计算机领域,硬件、软件种种调度才具都在不断的向上和变异。那么对于生机勃勃的前端来讲,调节和测试才具也特别显得至关心重视要。天猫前端共青团和少先队也正在选择一些立异的本事和手腕来消除有线页面调节和测试的标题。明日先跟大家大快朵颐下浏览器远程调节和测试技能,本文将用
Chrome/Webview 来作为案例。

Node

使用命令行

Node.js 控制台 REPL

在极端敲node进入repl

1+1
a = 1;

调养原理

相信有无数人和本人同壹,习贯了采取chrome调节和测试js程序,然则node刚起先提供的调节和测试格局只用Debugger,只好通过node
–debug
xxx.js运行命令行调节和测试工具,及其的不便宜。当然也有部分插件在此基础上,使用websocket实行通讯,使其能够在chrome浏览器中调和。体验和平素在chrome上开始展览调和照旧差了过多。

安装

因此npm进行设置

npm install chrome-remote-interface

基础知识

Node.js是起家在谷歌 Chrome V八引擎和ECMASC哈弗IPT之上的。

调解格局与权力管理

美高梅开户网址 4

时下正规浏览器调节和测试目的分为二种:Chrome PC 浏览器和 Chrome
Mobile(Android 四.四 以往,Android WebView 其实就是 Chromium WebView)。

唯独在Node v柒.x.x后,Node有提供了一个Inspector,能够直接和Chrome
DevTools进行通讯,上边来详细介绍进入调节和测试的手续,以及在运用进度中,作者遇上的主题材料及消除办法。

起步调节和测试目的

chrome-remote-interface基于chrome调节和测试协议,由此其帮忙调节和测试chrome浏览器和node运转景况。
甭管哪类调节和测试目标,其运维时都应有钦命调节和测试端口。
假使要调解chrome浏览器,应该在开发银行chrome时加多–remote-debugging-port参数,如下:

goole-chrome --remote-debugging-port=9222

要是调试node,在运营时增多–inspect参数,如下:

node --inspect=9222 app.js

此时,node会输出:

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

题外话:假使只是梦想调节和测试node,并不盘算开采二个调护治疗node的工具以来,依据提醒中的url,在chrome中张开就平素能够用chrome的开采工具调节和测试了。

命名

静态变量大概个体函数以_开头

Chrome PC 浏览器

对此调试 Chrome PC
浏览器,恐怕大家常常利用的是用鼠标右键也许急迅情势(mac:option + command

  • J),唤起 Chrome
    的调整台,来对近来页面举行调护治疗。其实还有别的一种办法,便是采纳贰个Chrome 浏览器调节和测试另一个 Chrome 浏览器。Chrome
    运维的时候,暗中同意是倒闭了调整端口的,假设要对二个对象 Chrome PC
    浏览器举行调解,那么运营的时候,可以经过传递参数来开启 Chrome
    的调养按键:

JavaScript

# for mac sudo /Applications/Google\
Chrome.app/Contents/MacOS/Google\ Chrome –remote-debugging-port=9222

1
2
# for mac
sudo /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome –remote-debugging-port=9222

目录

  1. 切实调节和测试步骤详细介绍
  2. 主题材料及消除方法
  3. 其它工具
  4. 连锁文书档案

体验命令行

  • 查看全体命令

    在顶峰输入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是针对浏览器的tab的命令,不要针对node来运行。
  • 翻看全数页面实例

    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的开拓工具,能够向来用该工具调试。和起步node时,node提醒的url是3个用场。
    id是目前页面标志。
    url是时下页面url。
    webSocketDebuggerUrl是node提供的ws协议的调节和测试服务,调节和测试客户端需求通过该url连接受调节和测试服务。

  • 查看调试目标版本

    chrome-remote-interface -t 127.0.0.1 -p 9222 version

    输出:

    [ { Browser: 'node.js/v7.0.0', 'Protocol-Version': '1.1' } ]
    
  • 翻开调节和测试目的援助的调节和测试协议

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol

    输出:

    { 
    remote: false,
    descriptor: {
        { version: { major: '1', minor: '2' },
        domains:[略]
    }
    }
    

    remote:false表明协议内容是工具自带的商业事务文本,并不是根源调节和测试目标。
    固然要博得调节和测试目标的商谈内容要增进-r选项,如:

    chrome-remote-interface -t 127.0.0.1 -p 9222 protocol -r

    输出:

    { 
    remote: true,
    descriptor: {
        { version: { major: '1', minor: '1' },
        domains:[略]
    }
    }
    
  • 调整命令

    chrome-remote-interface -t 127.0.0.1 -p 9222 inspect -r
    -r 代表应用调节和测试目标提供的商业事务描述文件
    经过inspect能够调弄整理node或chrome所帮助的各类域。

    Console域体验

Console.enable()
{ result: {} }
Console.clearMessages()
{ result: {} }
“`

Debugger域体验

```

Debugger.enable()
{ result: {} }
Debugger.setBreakpointsActive({active:true})
{ result: {} }
“`

HeapProfiler域体验

```

HeapProfiler.enable()
{ result: {} }
HeapProfiler.startTrackingHeapObjects({trackAllocations:true})
{ result: {} }
“`

Profiler域体验

```

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域体验  

>>> Runtime.evaluate({expression:"1+1"})
{ result: { result: { type: 'number', value: 2, description: '2' } } }

Schema域体验

>>> 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' } ] } }
    ```

    体验事件处理

    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调试协议连接调试目标

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - chooseTab 决定调试哪个tab。该参数可以为三种类型:
        - function 提供一个返回tab序号的函数
        - object 正如new 和 list返回的对象一样
        - string websocket url
        默认为一个函数,返回当前激活状态的tab的序号,如:function(tabs){return 0;}
    - protocol 协议描述符,默认由remote选项来定是否使用调试目标提供的协议描述符
    - remote 默认false,如果protocol设置了,该选项不会起作用

  callback   
    如果callback是函数类型,则该函数在connect事件发生后会得到回调,并返回EventEmitter对象,如果不是,则返回Promise对象。

    EventEmitter对象支持如下事件:  

    - connect

        ```
    function (chrome) {}
    ```
    当websocket连接建立后触发,chrome是Chome类型的实例。

    - disconnect

        ```
        function () {}
        ```
        关闭websocket连接时触发

    - error

        ```
        function (err) {}
        ```
    当通过host:port/json无法到达,或websocket连接无法建立时触发  
        err,Error类型的错误对象

  使用样例,针对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调试协议描述符

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false

    callback 回调函数,在获取到协议内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - protocol 包含以下属性
        - remote 是否远程协议
        - descriptor chrome调试协议描述符

  使用样例:

    ```
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,当然在node中只会有一个  

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - remote 默认false

    callback 回调函数,在获取到tabs内容后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tabs 获取到的tab数组

  使用样例:

    ```
const Chrome = require('chrome-remote-interface');
Chrome.List(function (err, tabs) {
    if (!err) {
        console.log(tabs);
    }
});
    ```

4. module.New([options], [callback])

    创建新的tab

    options object类型,具有如下属性:  
    - host 默认localhost
    - port 默认9222
    - url 新tab加载的url 默认about:blank

    callback 回调函数,在新tab创建后调用,函数接收如下参数:  
    - err Error一个错误对象,如果有错误的话
    - tab 新增的tab

  使用样例:  

    ```
const Chrome = require('chrome-remote-interface');
Chrome.New(function (err, tab) {
    if (!err) {
        console.log(tab);
    }
});
  1. module.Activate([options], [callback])

    激活钦赐tab

    options object类型,具备如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab创制后调用,函数接收如下参数:

    • err Error三个张冠李戴对象,借使有错误的话

应用样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Activate({‘id’: ‘CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC’}, function
(err) {
if (!err) {
console.log(‘success! tab is closing’);
}
});
“`

  1. module.Close([options], [中对Node程序开始展览调和,编制程序式调节和测试node程序的利器chrome。callback])

    关闭钦点tab

    options object类型,具有如下属性:

    • host 默认localhost
    • port 默认9222
    • id 目标tab的id

    callback 回调函数,在新tab开创后调用,函数接收如下参数:

    • err Error二个荒谬对象,若是有不当的话

运用样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Close({‘id’: ‘CC46FBFA-3BDA-493B-B2E4-2BE6EB0D97EC’}, function
(err) {
if (!err) {
console.log(‘success! tab is closing’);
}
});
“`

  1. module.Version([options], [callback])

    获得版本音讯

    options object类型,具备如下属性:

    • host 默认localhost
    • port 默认9222

    callback 回调函数,在新tab创造后调用,函数接收如下参数:

    • err Error2个谬误对象,如若有错误的话
    • info json object

采用样例:

```

const Chrome = require(‘chrome-remote-interface’);
Chrome.Version(function (err, info) {
if (!err) {
console.log(info);
}
});
“`

  1. Chrome 类

    帮衬的风云:

    • event

      在长途调节和测试目的发送公告时接触,一般是长途对象试行了客户端提交的点子后

      function (message) {}
      

      message包蕴如下属性:

      • method 公告内容,方法名 如:’Network.request威尔BeSent’
      • params 参数内容

    行使样例:

     ```
     chrome.on('event', function (message) {
         if (message.method === 'Network.requestWillBeSent') {
             console.log(message.params);
         }   
     });
     ```
    
    • <method>

      调养目的经过websocket发送了贰个钦点方法名的照顾

      function (params) {}
      

      应用样例:

      chrome.on('Network.requestWillBeSent', console.log);
      
    • ready

      历次未有调节和测试命令等待调节和测试目的重返时接触

      function () {}
      

      动用样例:

      只在Network和Page激活后加载二个url

      chrome.Network.enable();
      chrome.Page.enable();
      chrome.once('ready', function () {
          chrome.Page.navigate({'url': 'https://github.com'});
      });
      

协助的办法

  • chrome.send(method, [params], [callback])

      发送一个调试命令
    
      method 命令名称  
      params 参数  
      callback 当远程对象对该命令发送一个应答后调用,函数具有以下参数:  
      - error boolean 是否成功
      - response 如果error===true,返回一个error对象,{error:...},否则返回一个应答,{result:...}
    
      注意:在chrome调试规范里提到的id字段,在这里被内部管理不会暴露给用户
    

    使用样例:

      ```
      chrome.send('Page.navigate', {'url': 'https://github.com'}, console.log);
      ```
    
    • chrome..([params], [callback])

      是chrome.send(‘.‘, params, callback);的1种变形

      例如:

        chrome.Page.navigate({'url': 'https://github.com'}, console.log);
      
    • chrome..(callback)

      是chrome.on(‘.‘, callback)的变形

      例如:

        chrome.Network.requestWillBeSent(console.log);
      
    • chrome.close([callback])

      关闭与远程调节和测试指标的连接
      callback会在websocket关闭成功后调用

保存的首要词

  1. process
  2. global
  3. module.exports

Chrome Android 浏览器

对于调节和测试 Android 上的 Chrome 也许 WebView 要求一而再 USB
线。张开调节和测试端口的秘技如下:

JavaScript

adb forward tcp:9222 localabstract:chrome_devtools_remote

1
adb forward tcp:9222 localabstract:chrome_devtools_remote

跟 Chrome PC 浏览器区别的是,对于 Chrome Android
浏览器,由于数量传输是通过 USB 线而不是 WIFI,实际上 Chrome Android
创设的三个 chrome_devtools_remote 那一个 path 的 domain
socket。所以,下边一条命令则是经过 Android 的 adb 将 PC 的端口 922二 通过
USB 线与 chrome_devtools_remote 这些 domain socket
建立了一个端口映射。

实际调节和测试步骤详细介绍

__dirname和process.cwd 相对路线 要是像这么起步 $ node ./code/program.js.

双面包车型地铁渠道是不1致的

权限管理

谷歌(Google) 为了限制调试端口的交接范围,对于 Chrome PC
浏览器,调节和测试端口只接受来自 127.0.0.1 或者 localhost
的数据请求,所以,你不能通过你的地面机械 IP 来调解 Chrome。对于 Android
Chrome/WebView,调节和测试端口只接受来自于 shell
这几个用户数据请求,也便是说只好通过 USB 举行调节和测试,而不可能经过 WIFI。

一. Chrome DevTools和Node版本供给

  1. Chrome DevTools: 55+
    在地方栏中输入chrome://settings/help,查看Chrome版本

    美高梅开户网址 5

    Chroem版本

  2. Node.js: v7.x.x+
    在命令行中输入node –version拓展查看

    美高梅开户网址 6

    Node.js版本

主旨模块

  1. htpp
  2. util
  3. querystring
  4. url
  5. fs
  6. path
  7. crypto
  8. string_decoder

发端调节和测试

由此上述的调解格局的联网以及调治端口的展开,今年在浏览器中输入:

JavaScript

1
http://127.0.0.1:9222/json

将会看到类似上边包车型大巴始末:

JavaScript

[ { “description”: “”, “devtoolsFrontendUrl”:
“/devtools/inspector.html?ws=1二7.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a61九八e7aad六e”,
“id”: “ebdace60-d48贰-4340-b62二-a61玖八e七aad六e”, “title”:
“揭秘浏览器远程调节和测试本领.mdown—/Users/harlen/Documents”, “type”: “page”,
“url”: “”, “webSocketDebuggerUrl”:
“ws://127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e”
} ]

1
2
3
4
5
6
7
8
9
10
11
[
  {
    "description": "",
    "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e",
    "id": "ebdace60-d482-4340-b622-a6198e7aad6e",
    "title": "揭秘浏览器远程调试技术.mdown—/Users/harlen/Documents",
    "type": "page",
    "url": "http://127.0.0.1:51004/view/61",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e"
  }
]

当中,最要害的 二 个参数分别是 id 和 webSocketDebuggerUrl。Chrome
会为每种页面分配3个唯一的
id,作为该页面的绝无仅有标记符。大约对目的浏览器的有着操作都以内需带上这些id。

Chrome 提供了以下那个 http 接口调节目标浏览器

JavaScript

# 获取当前持有可调式页面新闻 # 获取调节和测试目的WebView/blink 的本子号 # 创设新的
tab,并加载 url # 关闭 id 对应的 tab

1
2
3
4
5
6
7
8
9
10
11
# 获取当前所有可调式页面信息
http://127.0.0.1:9222/json
 
# 获取调试目标 WebView/blink 的版本号
http://127.0.0.1:9222/json/version
 
# 创建新的 tab,并加载 url
http://127.0.0.1:9222/json/new?url
 
# 关闭 id 对应的 tab
http://127.0.0.1:9222/json/close/id

webSocketDebuggerUrl 则在调解该页面须求使用的多个 WebSocket 连接。chrome
的 devtool 的保有调节和测试成效,都以凭借 Remote Debugging
Protocol
使用 WebSocket 来张开数据传输的。那么那些 WebSocket,就是下边大家从
http://127.0.0.1:9222/json 获取的
webSocketDebuggerUrl,每二个页面都有温馨差异的
webSocketDebuggerUrl。这个 webSocketDebuggerUrl是由此 url 的 query
参数字传送递给 chrome devtool 的。

chrome 的 devtool 能够从 Chrome 浏览器中举办提取 devtool 源码可能从
blink 源码中收获。在配备好团结的 chrome devtool
代码之后,上边既能够发轫对 Chrome 实行调治将养, 浏览器输入一下内容:

JavaScript

1
http://path_to_your_devtool/devtool.html?ws=127.0.0.1:9222/devtools/page/ebdace60-d482-4340-b622-a6198e7aad6e

里面 ws 那些参数的值正是地点出现的 webSocketDebuggerUrl。Chrome 的
devtool 会利用这些 url 创立 WebSocket 对该页面举行调理。

二. 周转脚本,并走访调节和测试页面

全部步调:先依据具体情状,选用分裂的参数运营脚本;然后访问相应的url获取调节和测试页面包车型大巴造访地址;最终访问这些地方,进入调养页面。

在指令行中运营相应脚本,使用–inspect,或者–inspect-brk开启调节和测试开关,如node
–inspect path/xxx.js
或者node –inspect-brk
path/xxx.js
。下边依据不一样景色开始展览具体分析。

调试node.js程序

  1. Node.js Debugger 并倒霉用
  2. Node Inspector Chrome devtools的八个端口
  3. Webstrom IDE调节和测试 异常好用
  4. console.log

哪些促成 JavaScript 调节和测试

在进入 Chrome 的 devtool 之后,大家能够调出调控台,来查看 devtool 的
WebSocket 数据。这些里面有众多数码,作者那边只讲跟 JavaScript
调节和测试相关的。
美高梅开户网址 7

图中,对于 JavaScript 调节和测试,有一条11分首要的新闻,笔者灰湖绿选中的那条音讯:

JavaScript

{“id”:6,”method”:”Debugger.enable”}

1
{"id":6,"method":"Debugger.enable"}

下一场选中要调度的 JavaScript 文件,然后设置3个断点,大家再来看看
WebSocket 音信:
美高梅开户网址 8

devtool 像目标 Chrome 发送了 2 条消息

JavaScript

{ “id”: 23, “method”: “Debugger.getScriptSource”, “params”: {
“scriptId”: “103” } }

1
2
3
4
5
6
7
{
  "id": 23,
  "method": "Debugger.getScriptSource",
  "params": {
    "scriptId": "103"
  }
}

JavaScript

{ “id”: 24, “method”: “Debugger.setBreakpointByUrl”, “params”: {
“lineNumber”: 2, “url”:
“”,
“columnNumber”: 0, “condition”: “” } }

1
2
3
4
5
6
7
8
9
10
{
  "id": 24,
  "method": "Debugger.setBreakpointByUrl",
  "params": {
    "lineNumber": 2,
    "url": "https://g.alicdn.com/alilog/wlog/0.2.10/??aplus_wap.js,spm_wap.js,spmact_wap.js",
    "columnNumber": 0,
    "condition": ""
  }
}

那正是说收到这几条音讯之后,V八 做了些什么啊?
我们先来大约的看下 V八 里面包车型大巴一小段源码片段:

JavaScript

// V8 Debugger.cpp DispatcherImpl(FrontendChannel* frontendChannel,
Backend* backend) : DispatcherBase(frontendChannel),
m_backend(backend) { m_dispatchMap[“Debugger.enable”] =
&DispatcherImpl::enable; m_dispatchMap[“Debugger.disable”] =
&DispatcherImpl::disable;
m_dispatchMap[“Debugger.setBreakpointsActive”] =
&DispatcherImpl::setBreakpointsActive;
m_dispatchMap[“Debugger.setSkipAllPauses”] =
&DispatcherImpl::setSkipAllPauses;
m_dispatchMap[“Debugger.setBreakpointByUrl”] =
&DispatcherImpl::setBreakpointByUrl;
m_dispatchMap[“Debugger.setBreakpoint”] =
&DispatcherImpl::setBreakpoint;
m_dispatchMap[“Debugger.removeBreakpoint”] =
&DispatcherImpl::removeBreakpoint;
m_dispatchMap[“Debugger.continueToLocation”] =
&DispatcherImpl::continueToLocation;
m_dispatchMap[“Debugger.stepOver”] = &DispatcherImpl::stepOver;
m_dispatchMap[“Debugger.stepInto”] = &DispatcherImpl::stepInto;
m_dispatchMap[“Debugger.stepOut”] = &DispatcherImpl::stepOut;
m_dispatchMap[“Debugger.pause”] = &DispatcherImpl::pause;
m_dispatchMap[“Debugger.resume”] = &DispatcherImpl::resume;
m_dispatchMap[“Debugger.searchInContent”] =
&DispatcherImpl::searchInContent;
m_dispatchMap[“Debugger.setScriptSource”] =
&DispatcherImpl::setScriptSource;
m_dispatchMap[“Debugger.restartFrame”] =
&DispatcherImpl::restartFrame;
m_dispatchMap[“Debugger.getScriptSource”] =
&DispatcherImpl::getScriptSource;
m_dispatchMap[“Debugger.setPauseOnExceptions”] =
&DispatcherImpl::setPauseOnExceptions;
m_dispatchMap[“Debugger.evaluateOnCallFrame”] =
&DispatcherImpl::evaluateOnCallFrame;
m_dispatchMap[“Debugger.setVariableValue”] =
&DispatcherImpl::setVariableValue;
m_dispatchMap[“Debugger.setAsyncCallStackDepth”] =
&DispatcherImpl::setAsyncCallStackDepth;
m_dispatchMap[“Debugger.setBlackboxPatterns”] =
&DispatcherImpl::setBlackboxPatterns;
m_dispatchMap[“Debugger.setBlackboxedRanges”] =
&DispatcherImpl::setBlackboxedRanges; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// V8 Debugger.cpp
DispatcherImpl(FrontendChannel* frontendChannel, Backend* backend) : DispatcherBase(frontendChannel), m_backend(backend) {
    m_dispatchMap["Debugger.enable"] = &DispatcherImpl::enable;
    m_dispatchMap["Debugger.disable"] = &DispatcherImpl::disable;
    m_dispatchMap["Debugger.setBreakpointsActive"] = &DispatcherImpl::setBreakpointsActive;
    m_dispatchMap["Debugger.setSkipAllPauses"] = &DispatcherImpl::setSkipAllPauses;
    m_dispatchMap["Debugger.setBreakpointByUrl"] = &DispatcherImpl::setBreakpointByUrl;
    m_dispatchMap["Debugger.setBreakpoint"] = &DispatcherImpl::setBreakpoint;
    m_dispatchMap["Debugger.removeBreakpoint"] = &DispatcherImpl::removeBreakpoint;
    m_dispatchMap["Debugger.continueToLocation"] = &DispatcherImpl::continueToLocation;
    m_dispatchMap["Debugger.stepOver"] = &DispatcherImpl::stepOver;
    m_dispatchMap["Debugger.stepInto"] = &DispatcherImpl::stepInto;
    m_dispatchMap["Debugger.stepOut"] = &DispatcherImpl::stepOut;
    m_dispatchMap["Debugger.pause"] = &DispatcherImpl::pause;
    m_dispatchMap["Debugger.resume"] = &DispatcherImpl::resume;
    m_dispatchMap["Debugger.searchInContent"] = &DispatcherImpl::searchInContent;
    m_dispatchMap["Debugger.setScriptSource"] = &DispatcherImpl::setScriptSource;
    m_dispatchMap["Debugger.restartFrame"] = &DispatcherImpl::restartFrame;
    m_dispatchMap["Debugger.getScriptSource"] = &DispatcherImpl::getScriptSource;
    m_dispatchMap["Debugger.setPauseOnExceptions"] = &DispatcherImpl::setPauseOnExceptions;
    m_dispatchMap["Debugger.evaluateOnCallFrame"] = &DispatcherImpl::evaluateOnCallFrame;
    m_dispatchMap["Debugger.setVariableValue"] = &DispatcherImpl::setVariableValue;
    m_dispatchMap["Debugger.setAsyncCallStackDepth"] = &DispatcherImpl::setAsyncCallStackDepth;
    m_dispatchMap["Debugger.setBlackboxPatterns"] = &DispatcherImpl::setBlackboxPatterns;
    m_dispatchMap["Debugger.setBlackboxedRanges"] = &DispatcherImpl::setBlackboxedRanges;
}

你会发觉,V八 有 m_dispatchMap 那样一个 Map。专门用来拍卖全数JavaScript 调节和测试相关的处理。
当中就有本文将在注重讲述的:

  • Debuggger.enable
  • Debugger.getScriptSource
  • setBreakpointByUrl

这个都亟待在 V八 的源码中找到答案。顺便给我们推荐2个翻看 Chromium/V八最准确的措施是行使
https://cs.chromium.org,比 SourceInsight
还要便宜。

情况1

第一运行脚本,假使您的本子搭建http可能net服务器,你能够直接行使–inspect。如

let net = require('net');

// 创建一个net服务器
const server = net.createServer();

// 连接回调函数
server.on('connection', (conn) => {
    console.log('connection');
});

// 监听8080端口
server.listen(8080);

// 监听回调函数
server.on('listening', () => {
    console.log('listening')
});

美高梅开户网址 9

利用–inspect后显得结果

注:

  1. 上海教室是在v8.九.一版本时显得的结果,后边会一遍列举出别样版本下的结果。
  2. Debugger listening on
    ws://127.0.0.1:9229/890b7b49-c744-4103-b0cd-6c5e8036be95,当中给定的url并不是提供给咱们在Chrome浏览器中走访的地址,而是Node.js和Chrome之间打开通讯的的地点,它们经过websocket通过点名的端口举行通讯,从而将调节和测试结果实时展现在Chrome浏览器中。

然后,访问http://IP:port/json/list(其中IP就是主机的IP地址,常常为127.0.0.一,port则是端口号,默感到922九,这是Node提要求自由调整工具链接调节和测试的协议,能够因此命令行参数进行改变参数文书档案地址),会回到相应http请求的元数据,包含WebSocket
UHavalL,UUID,Chrome DevTools U翼虎L。在那之中,WebSocket
URL
尽管Node.js和Chrome之间的通信地点;UUID是二个一定的标记,每二个进程都会分配1个uuid,由此每3回调用会有出现分化的结果;Chrome
DevTools URL
就是大家的调节和测试页面包车型地铁url。

如访问http://127.0.0.1:9229/json/list,大家将会获取一下结果,当中devtoolsFrontendUrl正是大家必要拜访的地址。

美高梅开户网址 10

显示器快照 2017-11-2二 1四.3四.1三.png

最终,访问这么些地址后显得页面:

美高梅开户网址 11

调和页面

使用Node.js Debugger

node debug hello.js

next 快捷键n 跳到下3个讲话
cont 快速键c 跳到下1个断点
step s 进入function
out o 跳出function
watch

开辟浏览器

还是推行 curl

Debugger.enable

JavaScript

void V8Debugger::enable() { if (m_enableCount++) return;
DCHECK(!enabled()); v8::HandleScope scope(m_isolate);
v8::Debug::SetDebugEventListener(m_isolate,
&V8Debugger::v8DebugEventCallback, v8::External::New(m_isolate, this));
m_debuggerContext.Reset(m_isolate,
v8::Debug::GetDebugContext(m_isolate)); compileDebuggerScript(); }

1
2
3
4
5
6
7
8
9
void V8Debugger::enable() {
    if (m_enableCount++) return;
    DCHECK(!enabled());
    v8::HandleScope scope(m_isolate);
    v8::Debug::SetDebugEventListener(m_isolate, &V8Debugger::v8DebugEventCallback,
    v8::External::New(m_isolate, this));
    m_debuggerContext.Reset(m_isolate, v8::Debug::GetDebugContext(m_isolate));
    compileDebuggerScript();
}

那个接口的称谓叫 Debugger.enable,不过接到那条音信,V8其实就干了两件工作事情:

  • SetDebugEventListener:
    给 JavaScript 调节和测试安装监听器,并安装 v8DebugEventCallback
    那么些回调函数。JavaScript
    全体的调治将养事件,都会被那些监听器捕获,包含:JavaScript
    万分截至,断点甘休,单步调节和测试等等。
  • compileDebuggerScript:
    编译 V8 内置的 JavaScript 文件
    debugger-script.js。由于那文件相比较长,我那边就不贴出来了,感兴趣的同校点击那个链接实行查看源码。debugger-script.js
    重倘诺概念了一些针对性 JavaScript
    断点进行操作的函数,比如设置断点、查找断点以及单步调节和测试相关的函数。那么这一个
    debugger-script.js 文件,被 V八 进行编写翻译之后,保存在 global
    对象上,等待对 JavaScript 进行调和的时候,被调用。

    #### Debugger.getScriptSource

    在 Chrome 解析引擎解析到 ` 标签之后,Chrome 将会把 script
    标签对应的 JavaScript 源码扔给 V8 编译执行。同时,V8 将会对所有的
    JavaScript 源码片段进行编号并保存。所以,当 chrome devtool
    需要获取要调试的 JavaScript 文件的时候,只需要通过
    Debugger.getScriptSource,给 V8 传递一个 scriptId,V8 将会把
    JavaScript 源码返回。我们再回头看看这个图中的消息:
    ![](http://jbcdn2.b0.upaiyun.com/2016/10/cc8205b6b73c6aa787046a0a6c634ae7.png)
    上面 id 为 23 的
    scriptSource` 就是 V八 重回的 JavaScript
    源码,如此的话,大家就能够在 devtool 中看到我们要调整的 JavaScript
    源码了。

情况2(重要)

借使您的本子运营完之后直接结束进程,那么您要求接纳–inspect-brk来运维调节和测试器,那样使得脚本能够代码施行在此以前break,否则,整个代码直接运营到代码结尾,结束进度,根本不只怕举办调理。如:

function sayHi(name) {
    console.log('Hello, ' + name);
}

sayHi('yyp');
  1. 使用美高梅开户网址 ,–inspect:

    美高梅开户网址 12

    直接运用–inspect

直接使用–inspect,代码实行实现后,进度一贯停止,因而并没有艺术在展开调节和测试。

  1. 使用–inspect-brk:

    美高梅开户网址 13

    使用–inspect-brk

美高梅开户网址 14

访问127.0.0.1:9229

美高梅开户网址 15

调和页面

从上边叁张图中得以看来,此时该Node进程并不曾向来退出,并且我们得以经过访问http://127.0.0.1:9229/json/list获得页面url,并且在调试页面中大家也意识,程序在首先行break。

全面的您,可能还开采,与大家所写的程序分歧的是,调节和测试页面中,将我们的顺序包裹在贰个函数中,因为浏览器中不存在对应的靶子(实际原因能够自行去钻探)。

使用Node Inspector

npm install -g node-inspector

然后,启动node-inspector
node-inspector

在新的极端展开
node —debug-brk hello-debug.js or node —debug hello-debug.js

张开chrome浏览器 此外浏览器不可以

Debugger.setBreakpointByUrl

抱有绸缪专业都做好了,以往就可以伊始设置断点了。从地点的多少个图中,已经能够很精通的观察,Debugger.setBreakpointByUrl
给目的 Chrome 传递了2个 JavaScript 的 url 和断点的行号。

首先,V八 会去找,是或不是曾经存在了该 U昂CoraL 对应的 JavaScript 源码了:

JavaScript

for (const auto& script : m_scripts) { if (!matches(m_inspector,
script.second->sourceURL(), url, isRegex)) continue;
std::unique_ptr<protocol::Debugger::Location> location =
resolveBreakpoint( breakpointId, script.first, breakpoint,
UserBreakpointSource); if (location)
(*locations)->addItem(std::move(location)); } *outBreakpointId =
breakpointId;

1
2
3
4
5
6
7
8
9
for (const auto& script : m_scripts) {
  if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
    continue;
  std::unique_ptr<protocol::Debugger::Location> location = resolveBreakpoint(
    breakpointId, script.first, breakpoint, UserBreakpointSource);
  if (location) (*locations)->addItem(std::move(location));
}
 
*outBreakpointId = breakpointId;

V八 给具备的断点,创造二个 breakpointObject。并将那么些 braekpointObject 以
的花样存放在叁个 Map 里面,而这么些 Key,就是这些 JavaScript 文件的
UCRUISERL。看到此间,已经得以分解许多同校在调整 JavaScript
蒙受的三个难点:,>

多少同学为了制止页面包车型客车 JavaScript 文件不创新,对于有个别根本的
JavaScript 文件的 UPAJEROL 增加访问时间戳,对于那些增添了走访时间戳的
JavaScript 文件实行安装断点然后刷新调节和测试的时候,Chrome 会打字与印刷3个warnning,告诉您断点丢失。

案由很简短,在调节和测试的时候,V八 开掘这一个 breakpointMap 里面找不到对应的
breakpointObject,因为 U奇骏L 爆发了变化,这一个 brakpointObject
就丢掉了,所以 V八 就找不到了,不可能张开断点调节和测试。

依据大家的例行思维,你也许会认为 V八 会将断点设置在 C++
中,其实一同首自己也是那般感觉。随着对 V八的追究,让本身见到了自己时曾相识的某些函数名:

JavaScript

v8::Local<v8::Function> setBreakpointFunction =
v8::Local<v8::Function>::Cast( m_debuggerScript.Get(m_isolate)
->Get(context, toV8StringInternalized(m_isolate, “setBreakpoint”))
.ToLocalChecked()); v8::Local<v8::Value> breakpointId =
v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
.ToLocalChecked();

1
2
3
4
5
6
7
v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
    m_debuggerScript.Get(m_isolate)
    ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
      .ToLocalChecked());
v8::Local<v8::Value> breakpointId =
  v8::Debug::Call(debuggerContext(), setBreakpointFunction, info)
    .ToLocalChecked();

其中,m_debuggerScript,正是自笔者日前提到的 debugger-script.js。随着对
V八 Debugger 的愈来愈索求,笔者意识,V八 实际上对这么些对那个 breakpointObject
设置了 二 次。二次是经过在 C++ 中调用 m_debuggerScript 的 setBreakpoint
设置到 JavaScript 的 context 里面,也正是地点那段 C++
逻辑做的业务。另一遍是,m_debuggerScript 反过来将断点消息设置到了 V8的 C++ Runtime 中,为要调治的 JavaScript 的某1行设置三个 JavaScript
的回调函数。

其余工具

node-inspector
在应用那些工具在此之前,笔者直接在利用node-inspector,只要求运用npm install -g
node-inspector安装node-inspector命令行工具,然后在运用node-inspector
path/xxx.js就能够运营调节和测试。
不足之处:调度体验相比差,相对Chrome
DevTools使用,感觉不够流畅,并且在较高版本中,debugger被取消(v7.7.0+),进而不能运用,或者会报错(v八.玖.一版本直接报错)。

vsCode + webStorm
提供第一手调试功效(未使用过)。

检验文件改造

  1. forever
  2. nodemon
  3. supervisor
  4. up

断点命中

出于 V八 对 JavaScript 是立刻编写翻译实施的,未有生成
bytecode,而是直接扭转的 machine code
实施的,所以那么些断点回调函数也会棉被服装置到这么些 machine code 里面。

谈到底触发断点事件,也是 V八 的 C++ Runtime。当用户刷新大概直接实行JavaScript 的逻辑的时候,实际上是 V八 C++ Runtime 在运转 JavaScript
片段产生的 machine code,那一个 machine code
已经包括了断点回调函数了。1旦那些 machine code
里面包车型客车回调函数被触发,接着就会接触在此以前 Debugger.enable
设置的调治将养事件监听器 Debug伊夫ntListener 的回调函数。并赶回一条新闻给
Chrome 的 devtool,告诉 Chrome devtool,当前 JavaScript 被 pause
的行号。到此结束,3个断点就被打中了。

关于 JavaScript
断点命中,其实是三个很复杂的长河。后边有时间以来,会特意讲讲 JavaScript
断点命中的详细逻辑。

主题素材及缓慢解决格局

总结

浏览器的调度,最终都落脚到引擎:渲染引擎和 JavaScipt 引擎。那么对于
JavaScript 调节和测试来讲,难点就在于 V八 怎样给 JavaScript
某1行进行标志然后进行断点,那亟需有一些 V八 的文化。

2 赞 3 收藏 1
评论

美高梅开户网址 16

一. 网络古板的不合时宜设置形式做客地址

美高梅开户网址 17

老式设置方式

出现的难点,正是服从上述步骤操作之后,我们并未察觉Node
debugging的选项,此时,就不亮堂怎么进展下去了。在那边给出的演讲是,在新式版的Chrome浏览器中,Node
debugging不需求手动去运营了,而是私下认可正是足以选用的

一. 比不上Node版本出现的标题

个别在多少个大学本科子下进展了测试(v6.玖.2,v7.3.0,v八.9.1)

v6.玖.二 版本下使用境况

美高梅开户网址 18

命令行试行结果

美高梅开户网址 19

访问

那时候您会意识,命令行已经给你拜访Chrome的url,并且这些url和访问http://127.0.0.1:9229/json/list取得的devtoolsFrontendUrl属性值同样,不过,当你拜访这么些url时,浏览器却不可能显得调节和测试页面,此时,表达你的Node版本太低,需求使用越来越高版本。

v7.三.0 版本下利用意况

美高梅开户网址 20

命令行实践结果

美高梅开户网址 21

访问

美高梅开户网址 22

调度页面

此刻,命令行也提供了访问调节和测试页面包车型客车链接。

v八.九.一 版本下利用状态

美高梅开户网址 23

命令行实践结果

美高梅开户网址 24

访问

美高梅开户网址 25

调理页面

那会儿,命令行给定的的url并不是调治页面包车型大巴url,因而必须通过访问http://127.0.0.1:9229/json/list来赢得调试页面url。

连锁文档

Debugging
Guide
Debugging Node.js
Apps

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图