記一次react-devtools探索過程
為什麼要探究react-devtools?這一切都要源於一次痛苦的看程式碼過程,
剛來位元組的時候,要熟悉接觸公司內部的業務程式碼,剛開始是通過修復一些元件的簡單bug來上手熟悉,
上手熟悉就得先找到這個元件的檔案位置,但專案中元件的層層巢狀讓我難以找到具體位置,以至於頁面上隨便一個按鈕,找到它的位置就要花費一段時間了。
那麼,我們能不能自動化這個找的過程呢?回答是:可以,不過這是馬後炮了,筆者在這中途的調研過程中,經歷了很多曲折。
開始研究react-devtool
猜測可能react-devtool可能已經有跟蹤元件檔案位置的功能,因此一開始,我去把react-devtool上的所有按鈕都點了一遍,最終在view source功能按鈕上發現了可能的解決問題的可能
點選之後chrome的開發者工具跳轉到了原始碼標籤,並標識了按鈕渲染函式的位置
好傢伙,這和我想做的跟蹤檔案位置已經很接近了,不僅如此,看下方“第442行,第23列(從xxx.js)”對映到原始碼
綜上,我能明確發現view source這個功能的能力有:
1.獲取元件的渲染函式
2.跳轉到渲染函式的所在位置
3.可以獲取到檔案位置路徑和其所在的行和列
接下來就讓我們來驗證這三個能力是如何實現的。
因此筆者馬不停蹄的在github上搜索react devtools的開原始碼
react-devtool view source 原始碼
const viewElementSourceFunction = id => { const rendererID = store.getRendererIDForElement(id); if (rendererID != null) { // Ask the renderer interface to determine the component function, // and store it as a global variable on the window bridge.send('viewElementSource', {id, rendererID}); setTimeout(() => { // Ask Chrome to display the location of the component function, // or a render method if it is a Class (ideally Class instance, not type) // assuming the renderer found one. chrome.devtools.inspectedWindow.eval(` if (window.$type != null) { if ( window.$type && window.$type.prototype && window.$type.prototype.isReactComponent ) { // inspect Component.render, not constructor inspect(window.$type.prototype.render); } else { // inspect Functional Component inspect(window.$type); } } `); }, 100); } };
關鍵點在於chrome.devtools.inspectedWindow.eval這個api,其中執行了inspect(window.$type.prototype.render)
window.$type.prototype.render便是元件的渲染函式,具體是怎麼被註冊到全域性window上的,後面會提到。
現在我們先來具體講講inspect這個瀏覽器api,
很簡單,我們做個實驗就懂了,首先在控制檯輸入這行程式碼
let p=document.querySelector('p');
inspect(p);
執行了之後,我們會驚奇的發現,它從控制檯跳轉到了元素,並標識了p標籤所在的位置。
那,如果inspect的入參不是dom而是一個函式呢?
function func(){}
inspect(func)
這時候神奇的事情來了,控制檯跳轉到了函式a的定義位置,
現在我們再來說說window.$type到底是什麼東西。