作移動開發大多數的時候跟手機界面打交道,也就是說你只能在電腦上開發寫代碼,最終效果是確是在另一個終端看到的。雖然各類瀏覽器爲開發者提供了不少模擬手機設備的功能,這些功能整體來講基本能夠知足咱們對於調試移動設備的需求,可是模擬畢竟是模擬,它不能真正作到實現真機同樣的效果。常常遇到在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,功能強大,簡單易用。