在《前端工程師職業生涯中的三個問題》一文中,曾提到過「工具」的重要性。今天,就以去年開發的一個實際應用到工作中的瀏覽器插件為例,簡單闡述下在這一方面的具體實踐。
1. 背景#
在工作場景中,報表是一種常見的業務形態。老闆需要通過報表了解公司的經營狀態,財務需要通過報表掌握收支明細,產品運營需要通過報表了解用戶數據。從抽象層面上來看,種種報表均是數據與展示形式組合的產物。其中,
概念 | 解釋 |
---|---|
數據 | 顧名思義,也即後端返回的結構化數據 |
展示形式 | 則需要根據數據的不同、使用報表的目的不同而差異化的表現為常見的表格、餅圖、折線圖、柱狀圖等形式 |
也即:
如下圖:
從實現的角度來看,客戶端將用戶在界面設置的篩選條件,格式化成特定查詢語句後,交由後端做具體的數據處理,在數據返回到客戶端後,客戶端再將結構化數據按照展示需要進行特定格式化,從而展示在用戶面前。
可見,在客戶端側,技術模型並不複雜。客戶端所面臨的複雜度來源於:
- 如何將通用業務流程模型抽象化
- 報表種類繁多,導致頁面數量龐大
- 前後端交互傳輸的數據多、格式各異、數據處理流程個性化
一旦數量上去之後,比如一百多張報表,且隨著時間的流逝與人員的更迭,當前維護人員往往並不清楚前後端數據交換協議的細節以及格式化規則,這對於前端頁面的後期維護成本而言,可以說是陡然而升。
排查因傳參或取值類型的錯誤,花費了大量人力成本。因此,如果有一個工具,能夠一眼看到前端發送給後端的數據、以及頁面渲染時又是取的什麼值,在一定程度上便能夠降低問題排查的成本。
2. 目標#
明確了當前面臨的問題,下一步則需要確定一個具體的目標。對於上述闡述的小問題,目標是非常明確的:能夠提供一個工具或平台,幫助開發人員快速查看當前報表的具體請求參數和渲染取值
3. 方案#
首先最容易想到的便是侵入式地給頁面組件添加特定標識或按照某種約定命名,然後外部再去讀取相應的信息。
但此種做法侵入性太強,對本就 BUG 頻出的業務頁面及脆弱的開發人員而言,都無疑是雪上加霜。因此,無代碼侵入,變成了方案的首要前提。
使用 React
的同學一定熟悉 React Devtools
,在 Component
面板,我們能夠清晰的看到頁面的組件層級及每個組件對應的 props
。這便是靈感的來源,心想,在 DOM
與 React Component
之間一定存在著某種連接,才能夠做到 DOM
到 Component
以及 Component
到 DOM
的映射。如果能夠獲取到 Component
,也就獲取到了有關組件的所有信息。且是代碼無侵入的。
隨後,在 React 的源碼中看到使用隨機字符串生成key
的操作。打開控制台,果然,我們找到了這種映射關係
如上圖所示,每一個React DOM
元素都包含有以__react
(視ReactDOM
版本不同而不同)開頭的屬性,其中存儲著當前DOM
的VirtualDOM
也即FiberNode
信息。根據FiberNode
的定義,其中memoizedProps
中便存儲著渲染當前元素所使用的props
。
有了這層映射關係,在外層,就不需要關心業務以及React
的處理過程,只關注最後生成的DOM
結果,因為結果的對錯,是我們最關心的問題。然後,剩下的事情就是處理具體的業務場景,順水推舟了。
比如:
- 最終實現無侵入,還是通過瀏覽器擴展實現,但瀏覽器擴展與頁面不在一個執行環境中,無法讀取到
DOM
屬性,此時我們選擇通過擴展,向頁面注入JS
腳本,通過事件機制傳遞數據 React
不同版本稍微有些許差異的處理- 在獲取表格頭時,表頭是嵌套分組的,需要處理表頭嵌套問題
- 執行時機問題,每次數據變化時需要重新獲取
React Component
的最新實例,以獲取最新的值。在頁面中,引起數據變化的是請求,因此,在擴展中監聽網絡請求,從而做到數據的同步
最終效果如下:
此處只闡述了,具體思路,有了這一方面的思路與具體實踐後,該思路也運用在了目前正在主導的自動化測試中。後續有時間再一一道來。