React 源碼調試方式
正文
什麼?調試 React 源碼還有優雅和不優雅之分?
別著急,我們先來聽個故事:
東東是一名前端工程師,主要用 React 技術棧,用瞭多年之後想深入一下,所以最近開始看 React 源碼。
斷點調試
他把 react 和 react-dom 包下載瞭下來,在項目裡引入,開發服務跑起來後,打開 Chrome Devtools 打斷點調試。
這樣調試瞭一段時間之後,他有瞭一些困惑:
這樣調試是可以的,但是總感覺和源碼有段距離,因為調試的是 react-dom.development.js
搜索定位
而源碼裡這些邏輯是分散在不同的包裡的,所以就算搞懂瞭邏輯,也不知道這些邏輯在哪些包裡,隻能靠搜索來定位。
所以他就在想,是不是有更好的調試方式,能夠調試 React 最初的源碼呢?
於是,他跑來問我:光哥,你調試 React 源碼會有這些問題麼?你是怎麼調試的呀?
我說,確實,我最開始也是調試的 react-dom.development.js,但是現在已經能直接調試 React 最初的源碼瞭,而且是在 VSCode 裡調試的,點擊調用棧能直接打開對應的 React 源碼文件並定位到對應行列號:
哇哦,這就是我想要的調試效果,這是怎麼做到的呀。
想實現這樣的調試效果確實還有點復雜,我們一點點來看:
首先,我們要做到在 VSCode 裡調試 React 項目,而不是在 Chrome Devtools 裡,這樣才能做到直接打開對應的文件:
用 VSCode 調試 React 項目
我們用 create-react-app 創建一個 react 項目,然後 npm run start 跑起來。
Chrome Devtools 調試
這時候瀏覽器訪問就可以用 Chrome Devtools 調試瞭:
但我們的目標是在 VSCode 裡調試,所以要添加一個 VSCode 的 debugger 配置:
在根目錄下建一個 .vscode/launch.json 的文件,添加一個 chrome 類型的調試配置,輸入調試的 url。
然後點擊 debug 啟動:
這時候就可以在 VSCode 裡直接打斷點調試瞭:
用 VSCode 調試肯定會比 Chrome Devtools 方便一些。但這不是我們最主要的目的,現在調試的依然是 react-dom.development.js:
那怎麼調試 react 最初的源碼呢?
這就涉及到 sourcemap 的作用瞭:
sourcemap
JS 代碼經過編譯,會產生目標代碼,但同時也會產生 sourcemap。sourcemap 的作用就是映射目標代碼中的位置和源碼中的位置。
比如源碼中的第 3 行第 5 列的代碼對應著編譯後的第 1 行第 10 列的代碼。
類似這樣的映射有很多,經過編碼以後是這樣的:
在 js 文件最後一行,加上這樣一行註釋就可以關聯 sourcemap:
//# sourceMappingURL=http://example.com/path/to/your/sourcemap.map
調試工具支持解析 sourcemap 來映射調試的代碼位置到源代碼中的位置。
比如 chrome devtools 的 Sources 面板就會提示從哪個文件 source mapping 過來的,點擊鏈接還可以跳到映射之前的文件:
同樣,VSCode Debugger 也支持 sourcemap,有個 sourceMaps 的調試配置選項來開啟和關閉 sourcemap 功能,默認開啟。
那這麼說我們隻要讓 react-dom.development.js 關聯上 sourcemap,就能調試最初的 React 源碼瞭?
理論上是這樣的,但是現在下載的 react、react-dom 包裡都不帶 sourcemap,我們得把 React 源碼下載下來自己 build:
build 出帶有 sourcemap 的 react 包
npm 下載react包
用 npm 下載的 react 包是這樣的:
而我們需要的是帶有 sourcemap 的代碼,也就是這樣的:
這就要下載 react 源碼自己 build 瞭:
git clone https://github.com/facebook/react
下載下來的代碼執行 npm run build 就能看到 build 的產物:
這裡的 build/node_modules 下的 react 和 react-dom 包就是我們需要的。
但是現在 build 出的代碼並沒有帶 sourcemap,需要改造下 build 流程。
build 命令執行的是 ./scripts/rollup/build.js,打開這個文件做一些修改。
找到 rollup 的配置,添加一行 sourcemap: true,這個很容易理解,就是讓 rollup 在構建時產生 sourcemap:
再跑 npm run build,會報這樣的錯誤:
某個轉換的插件沒有生成 sourcemap。
這個是因為構建的過程中會進行多次轉換,會生成多次 sourcemap,然後把 sourcemap 串聯起來就是最終的 sourcemap。如果中間有一步轉換沒有生成 sourcemap,那就斷掉瞭,也就沒法把 sourcemap 串聯起來瞭。
插件註釋
這個問題的解決隻要找出沒有生成 sourcemap 的那幾個插件註釋掉就可以瞭:
在 getPlugins 方法裡,把這樣 4 個插件給註釋掉:
這個是刪除 use strict 用的,可以去掉。
這個是生產環境壓縮代碼的,也可以去掉。
這個是用 prettier 格式化代碼的,也可以去掉。
這個是添加一些頭部的代碼的,比如 Lisence 等,也沒啥用,可以去掉。
去掉這四個插件之後,再運行 npm run build,這時候就能正常進行構建瞭,然後產生的代碼就是帶有 sourcemap 的:
這樣我們就成功的 build 出瞭帶有 sourcemap 的 react 包!
調試 React 最初源碼
接下來隻剩最後一步,用上 sourcemap,實現直接調試 React 最初的源碼,
應用 sourcemap,調試 React 最初的源碼
我們已經 build 除瞭帶有 sourcemap 的 react 和 react-dom 包,那把這倆包復制到測試項目的 node_modules 下,就可以直接調試最初的源碼瞭麼?
還是不行。
為什麼呢?
看下面這張圖:
我們改造瞭 build 流程,對 react 源碼進行瞭 build,產生瞭帶有 sourcemap 的 react、react-dom 包,這些包最終導出的是 react-xx.development.js。
之後在項目裡引入,經過 webpack 打包,產生瞭 bundle.js 和 sourcemap。
之後調試工具運行代碼的時候,會解析 sourcemap,完成從 bundle.js 到 react-xxx.development.js 的映射:
但是並不會再次做 react-xx.development.js 到 react 最初源碼的映射呀。
也就是調試工具隻會解析一次 sourcemap。
那怎麼辦呢?
不打包 react 和 react-dom 這倆包不就行瞭。不經過 webpack 打包,那就沒有 webpack 產生的 sourcemap,不就一次就映射到 React 最初的源碼瞭麼。
那怎麼不打包這倆模塊呢?
webpack 支持 externals 來配置一些模塊使用全局變量而不進行打包,這樣我們就可以單獨加載 react、react-dom,然後把他們導出的全局變量配置到 externals 就行瞭。
要改動 webpack 配置的話,在 create-react-app 下要執行 npm run eject。
然後項目下會多出 config 目錄和 public 目錄,這倆分別放著 webpack 配置和一些公共文件。
修改 webpack 配置,在 externals 下添加 react 和 react-dom 包對應的全局變量:
然後把 react.development.js 和 react-dom.development.js 放到 public 下,並在 index.html 裡面加載這倆文件:
這樣再重新 debug,你就會發現 sourcemap 映射到 React 最初的源碼瞭:
不再是 react-dom.development.js 下的代碼,而是具體 react-xxx 包下的。
這就達到瞭最開始的目的,能直接調試 React 最初的源碼!
還記得我們這樣做的意義麼?
能調試最初的源碼才能知道哪段邏輯是在哪個包裡的,不然要自己去搜索。
這樣已經能夠達到我們的目的瞭,但是要想點擊調用棧直接定位到 git clone 下來的 react 項目的文件,還需要再做一步。
關聯 react 源碼項目
看我最初演示的效果,點擊調用棧是能直接定位到 react 源碼項目的文件的:
這是怎麼做到的呢?
其實隻要 sourcemap 生效,並且 map 到的文件是在當前 workspace 下,VSCode 就會打開對應的文件。
現在 sourcemap 已經生效瞭,隻不過 react 項目沒有在 workspace 下。所以,如果想直接定位 react 源碼項目的話,可以這樣做:
創建一個新的目錄,把 react 源碼項目和測試的項目放到一個 workspace 下,這樣再調試的時候,map 到的文件就能在 workspace 找到瞭,也就會打開相應的文件。
隻不過現在 sourcemap 下都是這樣的相對路徑,會導致映射到的文件路徑不對:
所以再去修改下 react build 流程,在 ./script/rollup/build.js 下,添加一個 sourcemap 的路徑映射,把 ../../../packages 映射到 react 項目的絕對路徑/pcakges :
這時候再重新 build,生成的 sourcemap 就是絕對路徑瞭:
把新生成的 sourcemap 復制過去,覆蓋一下。
在新的 workspace 裡 debug,你就會發現,路徑映射對瞭:
點擊調用棧能直接打開 react 源碼項目的對應文件瞭!
至此,我們就能優雅的調試 React 最初的源碼瞭。
總結
用瞭 react 比較長時間後,自然會想調試下源碼來深入下,但是常規的調試方式隻能調試 react-dom.development.js,雖然能理清邏輯,但是對應不到源碼裡的哪些包哪些文件,總感覺和最初的源碼還有一段距離。
這個問題是有解決方案的,就是會有點復雜:
首先要把 react 源碼項目下載下來,修改 build 流程來生成帶有 sourcemap 的 react 和 react-dom 包,並且修改 sourcemap 映射的路徑為絕對路徑。
然後把 react 和 react-dom 配置到 webpack 的 externals 裡,不進行打包,而是單獨在 index.html 裡引入。
因為 sourcemap 隻會映射一次,而 webpack 已經生成瞭一次 sourcmap,隻有跳過這倆模塊的打包才能讓 react 和 react-dom 的 sourcemap 生效。
之後用 VSCode Debugger 來調試 React 項目,就能映射到最初的 React 源碼瞭。
如果想點擊調用棧直接打開對應 React 源碼項目的文件,那就新建一個 workspace,把測試項目和 React 源碼項目包含就行瞭。因為 VSCode 如果在 workspace 下找到瞭 source map 到的文件,就會直接打開對應的文件。
東東:最終的調試效果是很完美,但這個流程有點復雜
我:確實,想實現能調試最初的源碼,並且還能直接打開對應的 react 源碼項目的文件,還是比較麻煩的,但好在隻需要配置一次,以後就能一直用瞭,而且類似的源碼調試方式也可以應用到其他源碼的調試。
毫不誇張地說,這應該是全網最優雅的 React 源碼調試方式瞭。
以上就是React 源碼調試方式的詳細內容,更多關於React 源碼調試的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- VSCode使React Vue代碼調試變得更爽
- 淺談React 的引入
- 基於visual studio code + react 開發環境搭建過程
- React服務端渲染原理解析與實踐
- 搭建React Native熱更新平臺的詳細過程