最全的chrome插件代碼調試

參考這份指南,結合自己手上的vue項目進行實踐,可以說對原指南進行了plus,因爲實踐過程中會有很多指南之外的新發現。

主要內容包括如下:

  • 預覽幾種不同的breakpoint類型
  • 代碼行級(Line-of-code)斷點

    • 代碼裏的某一行上打斷點
    • 有條件的行級斷點
    • 管理行級斷點
  • DOM變化級斷點

    • 幾種不同的DOM級斷點
  • XHR/Fetch斷點
  • 事件Listener斷點
  • Exception 斷點
  • Function 斷點

    • 確保目標函數在作用域中
  • Feedback

使用breakpoints去爲我們的JavaScript代碼打斷點。這個指南涉及了在DevTools上適用的每一種breakpoint類型,並且會講解如何使用並設置每種類型的斷點。如果是想學習如何在Chrome DevTools上調試代碼,可以看Get Started with Debugging JavaScript in Chrome DevTools

預覽幾種不同的breakpoint類型

衆人皆知的breakpoint類型是line-of-code。但是line-of-code型breakpoint有的時候沒法設置(其實就是沒法在代碼左邊點出一個綠點來),或者如果你正在使用一個大型的代碼庫。通過學習如何和何時使用這些不同類型的breakpoint debug,會大大節約你的時間。

斷點類型 當你想Pause的時候使用
Line-of-code 代碼具體某一行(其實就是沒法在代碼左邊點出一個綠點來
Conditional line-of-code 代碼具體某一行,但是只有在一些條件爲true時
DOM 在改變或者移除一個DOM節點或者它的DOM子節點時
XHR 當一個XHR URL包含一個string pattern
Event Listener 在運行了某個特定事件後的代碼上,例如click事件觸發
Exception 在拋出了一個caught或者uncaught的exception時
Function 當一個函數被調用時

Line-of-code breakpoints

如果你知道自己想在哪一具體的代碼行檢查代碼,會用到line-of-code breakpoint。DevTools會在這行代碼執行前暫停住。

爲了在DevTools某一行設置斷點:

  • 1.點擊Scources tab
  • 2.打開包含你想打斷點的文件
  • 3.跳到那一行
  • 4.點擊那一行的左邊,一個藍色或者綠色的圖標

image

圖1: 在某一行設置一個斷點

下面的圖是我本地調試的圖,只有部分有定義,解構,回調,return等有需要process的地方,纔可以打斷點。
image

代碼裏生成一個Line-of-code斷點

調用debugger在那一行暫停。debugger等價於一個line-of-code斷點,這可以讓端點直接出現在代碼中,而不是在DevTools UI中,可以在任何機器上進行debug。

console.log('a');
console.log('b');
debugger;
console.log('c');

有條件的行級斷點

在我們知道自己需要檢查的一個特定代碼行時,使用有條件的行級斷點,但是只有在一些其他滿足時纔可以暫停。
如何設置一個有條件的行級斷點:

  • 1.點擊Sources tab
  • 2.打開代碼文件
  • 3.跳到那一行
  • 4.在那一行Right-click,鼠標右鍵(此處有surprise!)
  • 5.選擇Add conditional breakpoint。一個對話框會出現在那一行代碼的下面。
  • 6.在對話框中鍵入條件
  • 7.按下enter去激活breakpoint。一個橘色icon會出現在左邊。

圖2:在某一行設置條件行級斷點

image

image
Add breakPoint是普通的行級斷點,Add conditional breakPoint是有條件的行級斷點。Never Pause Here會讓這裏永遠不進斷點。Blackbox Script可以讓當前打開的文件黑盒化,用不進入斷點。
點擊Add conditional breakPoint後,會出現dialog:
image
點擊Blackbox Script後,文件頭部會出現The script is blackboxed in debugger
image

在我的代碼中,添加了mediaType==='text'的條件,而mediaType還會有'img','mini'等類型,但是通過添加conditional breakpoint,我可以只在類型是text的時候pause,親測有效。
image

管理line-of-code斷點

圖3:Breakpoints面板展示了get-started.js的2個斷點

image

圖4:使用Breakpoints面板去禁用或者移除line-of-code斷點。

image
image
可以對單個斷點啓用,禁用,移除;對所有斷點啓用,禁用,移除;對除當前斷點外的斷點啓用,禁用,移除。

  • 選中checkbox禁用斷點
  • 右鍵移除斷點
  • Deactivate斷點,會保留他們激活時的狀態,這樣方便再次激活,右上角的label爲藍色激活狀態時,Breakpoints全部爲Deactive狀態,白色帶阻塞斜槓時,爲斷點激活狀態。

image
image

DOM變化級斷點

  • Subtree modifications。噹噹前選中的節點的child被移除或新增時觸發,或者是child的內容發生改變時。在child的節點屬性發生變化時不會觸發,或者當前節點改變時不會觸發。(親測當自己被移除時都不會觸發)
  • Attributes modifications。噹噹前選中的節點新增屬性,移除屬性,或者屬性值發生變化時觸發。
  • Node Removal。噹噹前選中的節點被移除時觸發。

在改變或者移除一個DOM節點或者它的DOM子節點時會用到DOM change breakpoint。
image
圖5:創建一個DOM change breakpoint的上下文菜單
設置DOM級斷點的步驟如下:

  • 1.單擊Elements tab
  • 2.選中我們想爲其設置breakpoint的元素
  • 3.右鍵元素
  • 4.懸浮在Break on上,選擇Subtree modifications,Attribute modifications或者Node removal。

我會在這樣一段代碼上測試Subtree modifications,Attribute modifications或者Node removal幾個DOM型斷點。

          <div class="url-cover-container">
            <cUpload accept="image/*" v-model="moment.params.url.cover" @finish="urlCoverUpload"
                     v-if="urlCoverUploadShow">
              <Button type="info">上傳封面</Button>
            </cUpload>
            <img v-else :src="moment.params.url.cover"/>
          </div>

渲染結果如下:

<div data-v-6eb44e66="" class="c-upload”>
    <input type="file" accept="image/*" style="display: none;”> 
    <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info”>
        <!——> // iview內部組件v-if else
        <!——> 
        <span>上傳封面</span>
    </button> 
    <!——> // v-if爲false,因此只保留一個註釋結束符
</div>

image
image

Subtree modifications

當我點擊上傳按鈕後,一次完整的上傳文件會引起5次DOM節點的變化:

<div data-v-6eb44e66="" class="url-cover-container">
    <div data-v-6eb44e66="" class="c-upload">
        <input type="file" accept="image/*" style="display: none;">
        <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info">
            <!---->
            <!---->
            <span>上傳封面</span>
        </button>
        <!---->
    </div>
</div>

image

<div data-v-6eb44e66="" class="url-cover-container">
    <div data-v-6eb44e66="" class="c-upload">
        <input type="file" accept="image/*" style="display: none;">
        <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info">
            <!---->
            <!---->
            <span>上傳封面</span>
        </button>
        // 下面的div.c-upload-loading-container就是div.c-upload新增的Descendant
         <div class="c-upload-loading-container">
            <div class="c-upload-loading">
                <svg data-v-65d9369a="" viewBox="25 25 50 50" class="circular">
                    <circle data-v-65d9369a="" cx="50" cy="50" r="20" stroke-dasharray="125.66370614359172" stroke-dashoffset="0"
                        class="path"></circle>
                </svg>
            </div>
        </div>
        <!---->
    </div>
</div>

image

<div data-v-6eb44e66="" class="url-cover-container">
    <div data-v-6eb44e66="" class="c-upload">
        <input type="file" accept="image/*" style="display: none;">
        <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info">
            <!---->
            <!---->
            <span>上傳封面</span>
        </button>
        <div class="c-upload-loading-container">
            <div class="c-upload-loading">
                <svg data-v-65d9369a="" viewBox="25 25 50 50" class="circular">
                    <circle data-v-65d9369a="" cx="50" cy="50" r="20" stroke-dasharray="125.66370614359172" stroke-dashoffset="-125.66370614359172"
                        class="path"></circle>
                </svg>
            </div>
        </div>
        // 這裏的<!-- --> 註釋DOM子Descendant節點被移除
    </div>
</div>

image

<div data-v-6eb44e66="" class="url-cover-container">
    // 下面的img就是div.url-cover-container新增的Child
    <img data-v-6eb44e66="" src="http://img.test.weidiango.com/FsHXLY5bu94-s6323xLHFSwh8GGM">
    <div data-v-6eb44e66="" class="c-upload">
        <input type="file" accept="image/*" style="display: none;">
        <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info">
            <!---->
            <!---->
            <span>上傳封面</span>
        </button>
        <div class="c-upload-loading-container">
            <div class="c-upload-loading">
                <svg data-v-65d9369a="" viewBox="25 25 50 50" class="circular">
                    <circle data-v-65d9369a="" cx="50" cy="50" r="20" stroke-dasharray="125.66370614359172" stroke-dashoffset="-125.66370614359172"
                        class="path"></circle>
                </svg>
            </div>
        </div>
    </div>
</div>

image

<div data-v-6eb44e66="" class="url-cover-container">
    // 這裏的div.c-upload就是被移除的Descendant
    <img data-v-6eb44e66="" src="http://img.test.weidiango.com/FsHXLY5bu94-s6323xLHFSwh8GGM">
</div>

child和descendant區別是什麼?
child僅僅包含一個子節點;descendant包含多個後代節點,會有一個孫子節點的情況,例如上面移除的#comment類型的註釋子節點。

Attribute Modifications

在3個狀態間切換,會觸發class屬性值的變化,因爲需要使激活的按鈕高亮。
image

<div data-v-6eb44e66="" name="ivuRadioGroup_1538124450566_3" class="ivu-radio-group ivu-radio-group-default ivu-radio-default ivu-radio-group-button">
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-default">
          圖文
    </label>
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-default">
          小視頻
    </label>
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-wrapper-checked ivu-radio-default ivu-radio-focus">
       圖文鏈接
     </label>
</div>

當狀態從圖文鏈接切換到小視頻時,會進入Attribute Modifications斷點,:
image

<div data-v-6eb44e66="" name="ivuRadioGroup_1538124450566_3" class="ivu-radio-group ivu-radio-group-default ivu-radio-default ivu-radio-group-button">
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-default">
        圖文
    </label>
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-wrapper-checked ivu-radio-default ivu-radio-focus">
        小視頻
    </label>
    <label data-v-6eb44e66="" class="ivu-radio-wrapper ivu-radio-group-item ivu-radio-wrapper-checked ivu-radio-default">
        圖文鏈接
    </label>
</div>

通過這次DOM Attribute Modification斷點,我們發現,iview的單選ButtonGroup高亮依賴.ivu-radio-focus,並且會爲之前選擇過的radio添加.ivu-raido-wrapper-checked。

因爲我們只對圖文鏈接label設置了Attribute Modification斷點,所以小視頻label的屬性變化不會被檢測到:
image

node removal

移除一個DOM節點時會進入斷點。
image
因此,其實就是subtree modifications中的最後一次DOM變化,把div.c-upload完全移除,只顯示一個上傳後的圖片。但是可能與subtree modifications的斷點提示不一樣,因此我將重新記錄斷點。

<div data-v-6eb44e66="" class="url-cover-container">
    // 下面的img就是div.url-cover-container新增的Child
    <img data-v-6eb44e66="" src="http://img.test.weidiango.com/FsHXLY5bu94-s6323xLHFSwh8GGM">
    <div data-v-6eb44e66="" class="c-upload">
        <input type="file" accept="image/*" style="display: none;">
        <button data-v-6eb44e66="" type="button" class="ivu-btn ivu-btn-info">
            <!---->
            <!---->
            <span>上傳封面</span>
        </button>
        <div class="c-upload-loading-container">
            <div class="c-upload-loading">
                <svg data-v-65d9369a="" viewBox="25 25 50 50" class="circular">
                    <circle data-v-65d9369a="" cx="50" cy="50" r="20" stroke-dasharray="125.66370614359172" stroke-dashoffset="-125.66370614359172"
                        class="path"></circle>
                </svg>
            </div>
        </div>
    </div>
</div>

image

<div data-v-6eb44e66="" class="url-cover-container">
    // 這裏的div.c-upload就是被移除的Descendant
    <img data-v-6eb44e66="" src="http://img.test.weidiango.com/FsHXLY5bu94-s6323xLHFSwh8GGM">
</div>

確實與之前的不同,直接提醒當前node被移除,而不是作爲Child或者Descendant被移除。

XHR/Fetch breakpoint

XHR的請求URL包含指定字符串時,使用XHR中斷。當XHR調用send()方法時在對應行暫停。
注意:這個特性同樣作用於Fetch請求。

在XHR/Fetch上打斷點的場景,非常適用於請求一個不正確的URL,而且你想很快找到AJAX或者Fetch 源代碼去發現導致錯誤請求的原因。

設置一個XHR類型的斷點的步驟如下:

  • 1.單擊Sources tab
  • 2.展開XHR Breakpoints
  • 3.單擊Add breakpoint
  • 4.鍵入你想斷點的string。DevTools會在string出現在任何XHR的請求的URL時進入斷點
  • 5.按下Enter進行確認

image
在XHR Breakpoints中爲包含URL中的org的任何請求創建XHR斷點

實驗:
image

當前頁有5個來自crm.test.foo.com的請求,每次都會進入到XHR斷點。

發出請求前進行斷點:
image

會進入到xhr.js中,開心地debug:
image

Event Listener breakpoints

如果要暫停事件觸發後運行的事件偵聽器代碼,請使用事件偵聽器斷點。我們可以選擇特定的事件,例如click事件;或者事件類別,例如所有的鼠標事件。

  • 1.點擊Sources tab
  • 2.展開Event Listener Breakpoints面板。DevTools展示了一個事件目錄的列表,例如Animation
  • 3.選中這些事件類別中的一個做斷點,這個斷點會在事件觸發時進入,或者是選擇事件類別下的一個或多個做斷點。

image
圖7:爲deviceorientation新建一個事件監聽斷點

有很多類型的Event Listener。
image

實驗:
image

會進入到第三方庫監聽事件的代碼,可以用Blackbox Script跳過進入這個文件的斷點,直接進入到我們的監聽click事件的業務代碼的地方,方便debug。結合CallStack和Scope,可以提升我們的debug效率。
image

如果想深入學習第三庫的源碼,可以打開斷點,每次都去看一次事件發生,第三方庫到底做了什麼。

Exception breakpoints

當我們想在某一行拋出caught or uncaught異常時進入斷點,可以使用exception breakpoint。

  • 1.單擊Sources tab
  • 2.單擊Pause on exceptions上,也就是

image。變藍了就表示開啓了,此時默認有未捕獲的異常時進入斷點。

  • 3.選中Pause On Caught Exceptions,可以使得捕獲的異常也進入斷點。

image
圖7:在一個未捕獲的異常處暫停
image
捕獲到一個CORS異常:
image

通過異常捕獲斷點,我們可以進入到axios,以及二次封裝的httpclient中,去看Exception是如何被處理的,出現問題時,可以精確定位。

有些時候可能不是我們業務代碼的bug,是第三方庫的bug,通過這樣的斷點,我們可以在業務代碼沒有問題的情況下,深入到第三方庫找問題(雖然一般都是自己業務代碼的問題)。

什麼是caught和uncaught的exception?

  • 通過throw,catch對exception做處理的,屬於caught exception
  • 沒有對異常做捕獲的exception,可能導致程序崩潰的exception,就屬於uncaught exception

親測:默認捕獲的異常時uncaught類型,開啓Pause on caught exceptions,會讓uncaught和caught類型的均進入斷點。

Function Breakpoints

假設我們想對某一個函數做debug的話,例如調用debug(functionNmae),functionName是我們想debug的函數。你可以在你的代碼中像插入console.log()一樣,插入debug(),或這在控制檯裏直接輸入debug()。debug()相當於在某個函數的第一行設置了一個line-of-code breakpoint。

function sum(a, b) {
  let result = a + b; // DevTools pauses on this line.
  return result;
}
debug(sum); // Pass the function object, not a string.
sum();

image

可以用來確保目標函數位於scope中
如果我們想要debug的函數不在當前作用域中,DevTools會拋出一個ReferenceError

(function () {
  function hey() {
    console.log('hey');
  }
  function yo() {
    console.log('yo');
  }
  debug(yo); // This works.
  yo();
})();
debug(hey); // This doesn't work. hey() is out of scope.

如果從DevTools控制檯調用debug(),確保目標函數在範圍內可能會很棘手,有一個辦法:

  • 1.函數作用域某一行設置一個line-of-code breakpoint。
  • 2.觸發斷點
  • 3.在斷點出暫停時調用debug()

嗯,上面這個方法很雞肋,親測。

實驗:

function foo(){
    function bar() {
    console.log('bar');
    }
    debug(bar);
    bar();
}
foo(); // 進入斷點
debug(bar); // 拋出Uncaught ReferenceError: bar is not defined

image
image

期待和大家交流,共同進步,歡迎大家加入我創建的與前端開發密切相關的技術討論小組:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章