說了一些 Chrome 開發者工具的技巧,其實並無涉及到開發者工具最核心的功能之一:斷點調試。斷點可讓程序運行到某一行的時候,把程序的整個運行狀態進行凍結。你能夠清晰地看到到這一行的全部的做用域變量、函數參數、函數調用堆棧。你能夠看到數據是怎麼在程序當中流動的,你還能夠修改、把玩它們。斷點調試讓你真正瞭解一個程序的運做流程。前端
聽聽亞洲舞王,著名 Web 前端工程師尼古拉斯·趙四是怎麼說的:
「斷點調試是檢驗一個前端工程師 debug 能力的惟一標準;是從初級前端工程師成爲中高級前端工程師的必經之路;是瞭解源碼和程序運行狀態的不二法門!」
看看 IBM 高級工程師,著名IT評論家,王博士怎麼說:
「面試一個前端工程師的時候,我通常給他一個bug,而後看他怎麼打斷點的,一我的的能力在調試過程當中一覽無遺。」
除了 debug 的時候會用到,斷點調試在你瞭解一個第三方庫源碼也頗有幫助。精通斷點調試的工程師在閱讀源碼的效率上比打 `console.log` 高效不止一個數量級。因此懂得如何斷點調試,你在 debug 能力、從源碼中學習新知識的能力(包括知識的量)會甩其餘工程師好幾條街。
接下來是小白教程,大神繞路。
=============================== 分割線 ==============================
下面是平常打斷點直播,跟着我一步步來,看看斷點調試如何可讓咱們瞭解一個程序的運行狀態、流程。這裏的例子是: 如何經過斷點調試,瞭解React.js 的 `setState` 方法到底幹了什麼事情。
創建一個 React 組件渲染到頁面上,App.js 以下:
import React, { Component } from 'react';react
class App extends Component {
constructor () {
super()
this.state = { name: 'World'}
}面試
handleClick () {
this.setState({ name: 'World 2' })
}windows
render() {
return (
<div onClick={this.handleClick.bind(this)}>
Hello {this.state.name}
</div>
)
}
}數組
export default App
程序很簡單:點擊 div,經過 `setState` 讓原來 「Hello World」 顯示成 「Hello World 2」。
Chrome 打開頁面,F12 或者 option + command + j (Mac 下) 打開 Chrome 開發者工具。點擊第三個 tab : Sources。瀏覽器
而後 command + o(windows 應該是 control + o) ,輸入 App.js 查找咱們的文件:前端工程師
很棒,第一個就是。而後回車,就能夠看到文件的內容:閉包
很明顯,這就是咱們上述的文件 App.js。點擊 div 的時候就調用 handleClick 方法。handleClick 方法內部有一個 setState 的操做,咱們在第 10 行的行號上點一下。app
而後點擊一下頁面上的 `Hello World`,函數
如今整個頁面卡住,什麼都操做不了。這是由於咱們的 JavaScript 已經徹底在第 10 行暫停了,整個應用程序是凍結的狀態。看看代碼右邊區域:
Watch 是幹嘛的?點擊 Watch 旁邊的➕,而後輸入 `this.state` 看看:
Watch 能夠幫你監控執行當前斷點所在做用域任何表達式的執行結果,輸入 `this.state.name + 'ok'` 看看:
下面 Call Stack 表示這個函數的調用堆棧,也就是 setState 是被哪一個上層函數調用的,上層函數又是誰調用的...
Scope 是當前斷點的做用域以及全部的閉包做用域以及全局做用域,你能夠看到全部做用域的變量。
右邊的功能很少說了。咱們看看怎麼繼續打斷點,鼠標放到代碼裏的 `setState` 函數上:
這裏彈出的信息告訴咱們,this.setState 是函數是在 `ReactComponent.js` 這個文件中定義的,並且仍是個匿名函數,它接受兩個參數,一個是 partialState,一個是 callback。直接點擊 `ReactComponent.js`,就會到 `setState` 函數定義的地方:
咱們看到 `setState` 的原型了,能夠看到它其實最主要調用 `this.updater.enqueueSetState` 函數。咱們在這一行上點打個斷點:
而後點擊瀏覽器上部的藍色箭頭:
這個箭頭指的是繼續執行代碼,代碼繼續執行,在咱們新打的斷點的地方又停住了:
咱們在旁邊能夠看到咱們傳進來的參數:
整個程序的變量猶如裸奔,一覽無遺。繼續把鼠標放到:
跳到 `ReactUpdateQueue.js` 文件:
繼續跳
繼續跳
你能夠發現,到這裏你能夠了解到:原來 React.js 的組件實例有兩種,一種是公共實例 `publicInstance`,一種是內部實例 `internalIntance`,內部實例存放在 ReactInstanceMap 當中,每次 `setState` 的時候,先取到內部實例。回到 ReactUpdateQueue.js ,咱們一怒之下又打了兩個斷點:
執行:
發現 React 把咱們的新的 state push 到了一個叫 queue 的數組中。如下省略 N 次斷點,到了:
又省略了 N 次斷點
...
最後你會了解當點擊頁面的時候,React.js 執行了一個叫 `dispatchEvent` 的方法,而後會 `batchedUpdates` 裏面執行 transaction.perform 一個事務;這個事務會執行一個函數,這個函數最終會調用咱們的 handleClick 方法使其得以執行;setState 的時候會把新的 state 放到一個更新隊列裏面,而且把 setState 組件放到一個叫 dirtyComponents 的隊列裏面;
transaction.perform 每次會執行之後都會執行 closeAll,因此當上述的操做執行完之後,會把 transaction 每一個 wrapper 都 close 掉。有一個 wapper 會調用 `runBatchedUpdates`,它會遍歷 `dirtyComponents` 而後一個個去執行 `updateComponent`,`updateComponent` 會從 state queue 拿出最新的 state 而後合併,最後更新頁面組件。
上面已經省略了不少細節。可是真實調試過程可能只須要 15 分鐘不到,你就能夠大體理解到 React.js 到底了發生了什麼事情。
這些事情你是用 console.log 無法快速瞭解到,直接看源碼可能也會一頭霧水。可是斷點調試,方便、快捷、簡單。你值得擁有。但實際上,這些事情對於受過較爲正統的 CS 專業的培訓的人來講,是習覺得常的事情,就像吃飯、呼吸同樣天然。然而最可怕的是:不少前端工程師都不知道。