作移动开发大多数的时候跟手机界面打交道,也就是说你只能在电脑上开发写代码,最终效果是确是在另一个终端看到的。虽然各类浏览器为开发者提供了不少模拟手机设备的功能,这些功能整体来讲基本能够知足咱们对于调试移动设备的需求,可是模拟毕竟是模拟,它不能真正作到实现真机同样的效果。常常遇到在chrome模拟器上样式显示正常,在手机上却出现样式错误的状况,在PC端模拟器运行正常,在真机上却报错的状况。因此,必须在想办法在真实的机子上测试咱们的代码最终的执行效果。移动端真机调试有几个刚需:看log、看error信息、看网络请求。其它的如实时修改html/css/javascipt代码, 查看timeline,cookie,localstorage,网络资源和断点调试就不是那么频繁。能够沿着两条思路解决这个问题:第一,本身写一个简单的调试工具,第二:寻找专业的调试工具。javascript
先介绍一下容易理解的移动端调试方法吧,好比本身写一个打印输出的信息框。php
/* * 移动端打印 * */ function debug(msg) { msg=new printTree().dump(msg); var div = document.querySelector("#logField"); if (div) { div.innerHTML += msg; } else { div = document.createElement('div'); div.id = "logField"; div.classList.add('debug'); // div.style['position'] = 'fixed'; // div.style['top'] = 0; // div.style['left'] = 0; div.style['width'] = '100%'; div.style['background-color'] = 'rgba(0,0,0,.8)'; div.style['font-size'] = '1.4rem'; div.style['color'] = 'yellow'; div.style['word-break'] = 'break-all'; div.style['line-height'] = '1.3'; div.style['padding'] = '10px 20px'; div.innerHTML = msg; var first=document.body.firstChild; document.body.insertBefore(div,first); // document.body.appendChild(div); } }
printTree源代码以下所示:css
1 ;(function(window){ 2 3 // Constructor 4 printTree = function (config){ 5 6 config=config ? config : {}; 7 8 this.tabKey= config.tabKey ? config.tabKey : " "; 9 10 }; 11 12 // Prototype 13 printTree.prototype = { 14 15 /** 16 * 17 * Private methods 18 * 19 */ 20 21 /** 22 * [对函数进行格式化处理] 23 * @param {[类型不肯定]} obj [数据内容] 24 * @param {[Number]} indent [缩进Tab键数量] 25 * @param {[Boolean]} addComma [是否须要在行尾加逗号] 26 * @param {[Boolean]} isArray [是否是数组] 27 * @param {[Boolean]} isPropertyContent [是否是属性内容] 28 * @return {[String]} print [格式化后的对象字符串] 29 */ 30 _printObject:function(obj, indent, addComma, isArray, isPropertyContent){ 31 32 var print = ""; 33 34 // 是否须要加逗号 35 var comma = (addComma) ? "," : ""; 36 37 // 判断对象类型 38 var type = Object.prototype.toString.call(obj); 39 40 // 数组的处理 41 if( type == "[object Array]"){ 42 43 // 44 if(obj.length == 0){ 45 // isPropertyContent若是为真,说明是属性内容,不用加tab键,不然要在前面加tab键 46 print += this._getRow(indent, "[ ]"+comma, isPropertyContent); 47 48 }else{ 49 50 // 打印数组前面的中括号 51 print += this._getRow(indent, "[", isPropertyContent); 52 53 for(var i = 0; i < obj.length; i++){ 54 // _printObject 数组中可能嵌套这其它对象 55 // indent + 1 每递归一次,加一个缩进符 56 // i < (obj.length - 1) 不是数组的最后一个元素,都要加逗号 57 print += this._printObject(obj[i], indent + 1, i < (obj.length - 1), true, false); 58 59 } 60 61 // 打印数组后面的中括号 62 print += this._getRow(indent, "]"+comma); 63 64 } 65 66 }else if(type == "[object Object]"){ 67 68 if(obj == null){ 69 print += this._formatLiteral("null", "", comma, indent, isArray); 70 } 71 else{ 72 73 var numProps = 0; 74 75 for(var prop in obj) numProps++; 76 77 if(numProps == 0){ 78 79 print += this._getRow(indent, "{ }"+comma, isPropertyContent); 80 81 }else{ 82 83 print += this._getRow(indent, "{", isPropertyContent); 84 85 var j = 0; 86 87 for(var prop in obj){ 88 89 print += this._getRow(indent + 1, prop+':'+this._printObject(obj[prop], indent + 1, ++j < numProps, false, true)); 90 91 } 92 93 print += this._getRow(indent, "}"+comma); 94 95 } 96 } 97 98 }else if(type == "[object Number]"){ 99 100 print += this._formatLiteral(obj,comma,indent, isArray); 101 102 }else if(type == "[object Boolean]"){ 103 104 print += this._formatLiteral(obj, comma, indent, isArray); 105 106 }else if(type == "[object Function]"){ 107 108 obj = this._formatFunction(obj); 109 110 print += this._formatLiteral(obj, comma, indent, isArray); 111 112 }else if(type == "[object Undefined]"){ 113 114 print += this._formatLiteral("undefined",comma, indent, isArray); 115 116 }else{ 117 118 print += this._formatLiteral(obj, comma, indent, isArray); 119 120 } 121 122 return print; 123 124 }, 125 126 /** 127 * [对文本内容进行格式化处理] 128 * @param {[String]} literal [字符内容] 129 * @param {[String]} comma [是否加逗号] 130 * @param {[Number]} indent [缩进的Tab键数量] 131 * @param {[Boolean]} isArray [是否为数组] 132 * @return {[String]} str [格式化后的函数字符串] 133 */ 134 _formatLiteral:function(literal, comma, indent, isArray){ 135 136 var str=literal+comma; 137 138 if(isArray){ 139 str = this._getRow(indent, str); 140 } 141 142 return str; 143 144 }, 145 146 /** 147 * [对函数进行格式化处理] 148 * @param {[Function]} obj [函数内容] 149 * @return {[String]} str [格式化后的函数字符串] 150 */ 151 _formatFunction:function(obj){ 152 153 154 var str = ""; 155 // 以换行符对每行进行分割 156 var funcStrArray = obj.toString().split("\n"); 157 158 if(funcStrArray.length){ 159 160 // 重组每行内容,除末尾行以外,每行加上换行符 161 for(var i = 0; i < funcStrArray.length-1; i++){ 162 str += funcStrArray[i] + "\n"; 163 } 164 return str+funcStrArray[i]; 165 }else{ 166 return str; 167 } 168 169 }, 170 171 172 /** 173 * [给每行加上缩进和换行] 174 * @param {[Number]} indent [每行须要缩进的TAB数量] 175 * @param {[String]} data [属性名] 176 * @param {[Boolean]} isPropertyContent [是否为属性内容] 177 * @return {[String]} tabs+data [格式化后的行内容] 178 */ 179 _getRow:function(indent, data, isPropertyContent){ 180 181 var tabs = ""; 182 183 // 计算属性名称前面的tab键数量 184 for(var i = 0; i < indent && !isPropertyContent; i++){ 185 tabs += this.tabKey; 186 } 187 188 // 给属性名加上换行(内容不为空且末尾不含换行符) 189 if(data != null && data.length > 0 && data.charAt(data.length-1) != "\n"){ 190 data = data+"\n"; 191 } 192 193 return tabs+data; 194 195 }, 196 /** 197 * 198 * Public methods 199 * 200 */ 201 dump:function(object){ 202 203 try{ 204 return this._printObject(object, 0, false, false, false) ; 205 }catch(e){ 206 alert("object语法错误,不能格式化,错误信息:\n"+e.message); 207 } 208 209 }, 210 211 }; 212 213 if (typeof exports !== 'undefined'){ 214 exports.printTree = printTree; 215 }else{ 216 window.printTree = printTree; 217 } 218 219 })(window);
这个方法有个缺点是没法打印[HTML DOM Element],html
好比一个实际的DOM 元素为java
<input type="email" name="email" id="email" placeholder="请输入企业邮箱帐号">
经过上述方式打印出来的效果为图2所示。相似的工具还有vconsolenode
安装方法web
npm install vconsole
使用方法---在须要调试的页面引入下面的js文件正则表达式
<script src="path/to/vconsole.min.js"></script>
<script> console.log('Hello world'); </script>
这个工具的实现原理,是很简单的函数劫持。其大概原理是建立一个和现有函数同名的函数(固然首先要把原来的函数给保存下来),以覆盖掉他本来的引用,而后在函数体内先针对参数作一些本身想要实现的功能,最后再调用以前保存的原函数,实现本来的功能。chrome
vconsole工具重写了window.XMLHttpRequest和window.console方法
npm
使用专业的工具
weinre全称是web insperctor remote,是一种远程调试工具,能够在PC上调试运行在移动设备上的远程页面。
Weinre 是一个至关简单好用的调试工具。它会在你本地建立一个监听服务器,并提供一个 JavaScript脚本,你只须要在须要测试的页面中加载这段 JS,就能够被 Weinre 监听到,在 inspect 面板中调试你这个页面。
如上图, Weinre由三部分组成,第一部分是运行在PC上的Debug Server, 它会与其余两部分交互,在测试页引入的那个target.js文件就存在于这个Server里
第二部分是Debug Client, 这个就是咱们上面一直在使用的运行在chrome中的调试客户端,它与Debug Server进行链接,并提供调试接口给用户。
第三部分是Debug Target, 也就是运行在咱们远程设备浏览器中的target.js, 它经过XHR与Server链接交互,将咱们的代码暴露给Server, 来实现DOM Inspection与修改。
在命令行启动weinre就开启了Debug Server, 而后在浏览器中输入http://ip:weinre端口就打开了Debug Client, 在调试页面中嵌入target.js代码, 在手机中打开页面, 就开启Debug Target。
weinre 的具体使用方法以下:
首先全局安装 weinre:
npm install -g weinre
安装完成以后,在命令行下启动weinre监听服务器,启动监听服务器以前,须要获取本机的局域网地址:
ipconfig getifaddr en0
命令。ipconfig
命令。这时候拿到本机IP,好比个人机器IP 为 192.168.201.54,这时候执行:
weinre --boundHost 192.168.201.54 --httpPort 10000
weinre支持的参数有:
--help : 显示Weinre的Help
--httpPort [portNumber] : 设置Weinre使用的端口号, 默认是8080
--boundHost [hostname | ip address | -all-] : 默认是'localhost', 这个参数是为了限制能够访问Weinre Server的设备, 设置为-all-或者指定ip, 那么任何设备均可以访问Weinre Server。
--verbose [true | false] : 若是想看到更多的关于Weinre运行状况的输出, 那么能够设置这个选项为true, 默认为false;
--debug [true | false] : 这个选项与--verbose相似, 会输出更多的信息。默认为false。
--readTimeout [seconds] : Server发送信息到Target/Client的超时时间, 默认为5s。
--deathTimeout [seconds] : 默认为3倍的readTimeout, 若是页面超过这个时间都没有任何响应, 那么就会断开链接。
开启本地监听服务器
复制http://192.168.201.54:10000,粘贴到浏览器地址栏,打开服务器网址,
复制监听脚本到须要被监听页面
当咱们有真机访问被调试页面的时候,被调试页面就会出如今监听列表中,若是有多个网页,你能够从列表中选择一个。而后就可使用后面的 Elements、Console 等面板来进行调试操做了:
首页RemoteTab由三部分组成, Targets是注册的远程设备列表, 当前咱们尚未访问测试页面, 因此Targets列表为none, Clients是Weinre客户端, 也即打开这个Weinre页面的设备列表。ServerProperties就是咱们运行Weinre时的一些配置项。
weinre 很是灵活,跨平台(Android、iOS 、Window Phone 都支持),跨浏览器(chrome,safari,国内各类浏览器均可以用), 可让咱们在电脑上直接调试运行在手机上的远程页面。在调试移动设备时须要在本地搭建一个局域网 IP 的服务器,将设备与本机网络链接成一个局域网,用移动设备访问这个网页便可。固然 Weinre 也不是万能的,相比 Chrome 的调试工具,它缺乏 JavaScript 断点以及 Profiles 等经常使用功能,可是它兼容性强,能够实现基础调试功能。
weinre兼容性挺强,并且能支持微信端页面的调试,到此为止,若是页面请求使用的是http,那weinre已经能够解决调试问题了。
可是若是要调试https请求的页面,仅仅使用weinre没法解决,由于在页面中须要引入调试的js文件,weinre启动的是http服务,因而使用反向代理软件ngrok,它能够作地址映射,并支持http/https/tcp等,使用也比较简单:
这是官网下载地址:https://ngrok.com/download,或者能够直接使用npm下载:
npm install -g ngrok
而后启动
ngrok http 192.168.201.54:9999
启动后能够打开 http://127.0.0.1:4040 查看链接信息:
在html中引入下面的js文件
注意:
1.绑定端口必定不能与本地环境已监听的端口冲突。本地我已监听了8080端口,因此我绑定的是10000端口。
2.boundHost默认为localhost,只能本地PC上用http://localhost:8080来访问,将localhost换作本地ip就没法打开Weinre调试工具,为了能在其余设备以及本地设备用ip打开Weinre调试工具,须要设置boundHost为"-all-"或者ip.
3.监听https页面时,要先启动weinre,再启动ngrok.
JSConsole至关于一个简化版的weinre,专一于console功能,它相对于Weinre的优势就是提供了现成了线上Debug Server与Debug Client, 无需用户在PC本地运行Debug服务, 只要在须要调试的页面像Weinre同样加入一个target库, 就能够在JSConsole官网上调试这个页面了。JSConsole的网址为https://jsconsole.com/。
接着在打开的网页输入 :listen
,将会获得一串 GUID 以及一对带有 src 属性的 Javascript 标签,以下图:
将这个 Javascript 脚本插入到须要调试的 html 页面中,好比这样:
复制代码<script src="http://jsconsole.com/remote.js?BDA15940-A201-4EAB-9482-941CD41742EC"></script> <script> var a = 1 , b = 2; console.log(a + b); </script>
而后刷新你本地须要调试的页面(PC端或者移动端),若是是第一次打开的话,会弹出下图内容,大概意思就是告诉你如今引入了 JSConsole 的一段 js 进行调试,记得在上线时将它移除。
在打开 JSConsole 的页面便会输出 console 的内容;若是页面 JS 报错,通常状况下也能在 JSConsole 中进行定位。
须要注意的是,刷新的是本地页面,而并非 JSConsole 的页面,一旦刷新 JSConsole 的页面,便会生成一个新的 GUID,这样以前生成的就没用了,调试也就失效了
一、一站式页面调试工具,远程调试任何手机浏览器页面,任何手机移动端webview(如:微信,HybirdApp,手机浏览器等)。 二、spy-debugger内部集成了weinre。 三、同时支持HTTP/HTTPS页面的调试。
安装
Windows 下
npm install spy-debugger -g
Mac 下
sudo npm install spy-debugger -g
安装后在命令行下输入 spy-debugger,启动代理服务器
设置手机代理
若是要监听https页面,你还须要作以下操做:
第一步:生成证书
spy-debugger initCA
// 证书生成在用户根目录的node-mitmproxy文件夹下的 // 如: /Users/wuchangming/node-mitmproxy
第二步:安装证书
把node-mitmproxy文件夹下的 node-mitmproxy.ca.crt 传到手机上,点击安装便可。
Fiddler是最强大最好用的Web调试工具之一,它能记录全部客户端和服务器的http和https请求,分析请求数据、设置断点、调试web应用、修改请求的数据,甚至能够修改服务器返回的数据. 使用Fiddler不管对开发仍是测试来讲,都有很大的帮助。
Fiddler 是以代理web服务器的形式工做的,它是在web server 和 client 之间搭了一层 proxy,全部的请求都会通过它。在打开它的那一瞬间,它就已经设置好了浏览器的代理了。它使用代理地址:127.0.0.1, 端口:8888.当你关闭的时候,它会自动注销代理服务,不过若是Fiddler非正常退出,会形成网页没法访问。 这是由于Fiddler没有自动注销,解决的办法是从新启动下Fiddler..
每种图标表明不一样的相应类型,具体的类型包括:
Inspectors tab下有不少查看Request或者Response的消息。分为上下两个部分,上半部分是请求头部分,下半部分是响应头部分。对于每一部分,提供了多种不一样格式查看每一个请求和响应的内容。
JPG 格式使用 ImageView 就能够看到图片,
HTML/JS/CSS 使用 TextView 能够看到响应的内容。
其中Raw Tab能够查看完整的消息,
Headers tab 只查看消息中的header.
Auth则能够查看受权Proxy-Authorization 和 Authorization的相关信息。
Cookies标签能够看到请求的cookie和响应的set-cookie头信息。
默认状况下,fiddler是不会捕获https会话的,因此须要自行设置下。启动软件,点击tools->Fiddler Options,在弹出框选择“HTTPS”,以下页面,将捕获HTTPS链接这一项前面全打钩,点击ok便可操做成功。
Tools --> Telerik Fiddler Options --> 选择https,这个面板的配置项有:
Capture HTTPS CONNECTs:捕获https链接
Decrypt HTTPS traffic:解密HTTPS通讯
Ignore servercertificate errors:忽略服务器证书错误
Fiddler最强大的功能莫过于设置断点了,设置好断点后,你能够修改httpRequest 的任何信息包括host, cookie或者表单中的数据。设置断点有两种方法
第一种:打开Fiddler 点击Rules-> Automatic Breakpoint ->Before Requests(这种方法会中断全部的会话)
如何消除命令呢? 点击Rules-> Automatic Breakpoint ->Disabled
第二种: 在命令行中输入命令: bpu www.baidu.com (这种方法只会中断www.baidu.com)
如何消除命令呢? 在命令行中输入命令 bpu
固然Fiddler中也能修改Response
第一种:打开Fiddler 点击Rules-> Automatic Breakpoint ->After Response (这种方法会中断全部的会话)
如何消除命令呢? 点击Rules-> Automatic Breakpoint ->Disabled
第二种: 在命令行中输入命令: bpafter www.baidu.com (这种方法只会中断www.baidu.com)
如何消除命令呢? 在命令行中输入命令 bpafter,
Fiddler比较重要且比较强大的功能之一。可用于拦截某一请求,并重定向到本地的资源,或者使用Fiddler的内置响应。可用于调试服务器端代码而无需修改服务器端的代码和配置,由于拦截和重定向后,实际上访问的是本地的文件或者获得的是Fiddler的内置响应。当勾选allow autoresponser 并设置相应的规则后(本例中的规则是将http://blog.csdn.net/ohmygirl的请求拦截到本地的文件layout.html),以下图所示
所以,若是要调试服务器的某个脚本文件,能够将该脚本拦截到本地,在本地修改完脚本以后,再修改服务器端的内容,这能够保证,尽可能在真实的环境下去调试,从而最大限度的减小bug发生的可能性。
不只是单个url,Fiddler支持多种url匹配的方式:
I. 字符匹配
如 example能够匹配 http://www.example.com和http://example.com.cn
II. 彻底匹配
以EXACT开头表示彻底匹配,如上边的例子
EXACT:http://blog.csdn.net/ohmygirl
III. 正则表达式匹配
以regex: 开头,使用正则表达式来匹配URL
如:regex:(?insx).*\.(css|js|php)$ 表示匹配全部以css,js,php结尾的请求url
老版本的fiddler中叫request-builder.顾名思义,能够构建相应的请求,有两种经常使用的方式构建请求:
(1)Parsed 输入请求的url以后executed便可,也能够修改相应的头信息(如添加经常使用的accept, host, referrer, cookie,cache-control等头部)后execute.
这个功能的常见应用是刷新页面的访问量
(2)Raw。使用HTTP头部信息构建http请求。与上相似。很少叙述
Fiddler的左下角有一个命令行工具叫作QuickExec,容许你直接输入命令。
常见得命令有
help 打开官方的使用页面介绍,全部的命令都会列出来 http://docs.telerik.com/fiddler/knowledgebase/quickexec
cls 清屏 (Ctrl+x 也能够清屏) select 选择会话的命令 ?.png 用来选择png后缀的图片 bpu 截获request
经过陈列出全部的HTTP通讯量,Fiddler能够很容易的向您展现哪些文件生成了您当前请求的页面。使用Statistics页签,用户能够经过选择多个会话来得来这几个会话的总的信息统计,好比多个请求和传输的字节数。
选择第一个请求和最后一个请求,可得到整个页面加载所消耗的整体时间。从条形图表中还能够分别出哪些请求耗时最多,从而对页面的访问进行访问速度优化
第一个选项zone有三种选择:no zone;仅显示局域网主机;仅显示互联网主机;
第二个选项Host有四种选择,能够在下面文本框输入内容,根据输入内容显示或不显示相应的主机;
“No Host Filter”不设置hosts过滤
“Hide The Following Hosts”隐藏过滤到的域名
“Show Only The Following Hosts”只显示过滤到的域名
“Flag The Following Hosts”标记过滤到的域名
Action按钮能够保存过滤规则集,载入过滤规则集等;
能够选择仅显示来自某些进程的流量;仅显示chrome流量;
仅显示包含某些字符的URL;隐藏包含某些字符的URL;显示头部包含某些标志的包;
还能够删除和设置请求头;此功能还没有用过;待用过以后再发文详述;
在post时中断请求;
在带查询参数的GET请求时中断;
在异步请求时中断;
获取到某种类型的响应时中断;
隐藏成功的包;
隐藏不成功的包;
2字头的状态码,表明请求已成功被服务器接收、理解、并接受;
隐藏须要用户验证的请求;
隐藏重定向请求;
默认显示所有类型;其余还有image,html,texe/css,scripts等类型;
捕获设置了cookie的响应头;
捕获响应头带某些字符串的包等;
HTTP抓包分析工具备比较多,如wireshark, FireBug,HttpWatch,Tcpdump,PAW(mac)等。在作移动端开发时,找到一款合适本身的能进行移动设备HTTP抓包的工具也是很是重要的。正所谓,工欲善其事必先利其器。
我的很是喜欢Fiddler和spy-debugger,功能强大,简单易用。