動態

詳情 返回 返回

給螞蟻金服 antv 提個 PR, 以為是改個錯別字, 未曾想背後的原因竟如此複雜! - 動態 詳情

前言

什麼? 你不瞭解G2Plot? 沒關係, 今天咱們要分享的內容和G2Plot的關係, 就像雷鋒和雷峯塔的關係. 因此, 不必擔心聽不懂. 我一直覺得, 如果我寫的文章有人看不懂, 那一定是我寫的不夠好. 而不是讀者的問題.

業務背景

最近在使用G2Plot雙軸圖開發業務. 大概長下面這個樣子.

image-20220818162350608

既然要開發雙軸圖, 那麼先看一遍文檔是正常操作. 於是我就打開了他的官網, 看到了這樣的一段話, 以及demo鏈接.

image-20220818163051799

看完後似懂非懂, 這不有DEMO嗎? 那我們就點擊看看效果.

兄弟們! 好像不太對啊. 雖然我才剛剛開始看雙軸圖, 對它的業務不是特別瞭解. 但是我不瞎啊. 這標題不是寫着面積圖嗎? 而且左側分類也屬於面積圖. 好傢伙, 在我眼皮底下狸貓換太子?

顯然, 這是路由跳轉有問題. 要不然我就提個PR幫他們改了吧. 這種鏈接錯誤、錯別字的PR, 本來我是不屑提的. 但是不知道為什麼, 冥冥之中就覺得這個PR和我八字相剋, 那就不好意思了, 老夫今天就替天行道了!

點擊查看圖片來源

找到鏈接錯誤之處

那麼既然要提PR, 首先我得知道這個正確的鏈接應該是啥樣的. 於是我觀察了下面積圖的第一個demo的鏈接

image-20220818164810027

顯然, 路由裏面主要有3個部分area/basic#basic, area/basic其實大家都好理解, 為什麼最後還要+個哈希參數呢? 簡單思考後很容易想到, 因為basic下面有好多的例子, 不同的demo對應不同的哈希. 我們戳第二個demo試試看.

image-20220818164948407

果然! 只有#後面的東西變化了. 接下來我們找到一開始時, 我們點擊文檔跳轉的鏈接https://g2plot.antv.vision/zh/examples/dual-axes/dual-line顯然, 就是少了哈希參數. 我們為其+上小尾巴應該就可以正常訪問了.

大功告成! 接下來我們要做的就是找到源碼中的跳轉之處替換鏈接即可.

img

事情好像沒有那麼簡單

image-20220818171037980

打開github, clone倉庫後, 搜索結果, 一氣呵成. 接下來我要做的事情就是替換這個markdown中的地址. 一想到就這樣輕輕鬆鬆的解決了一個bug, 我露出了邪惡的笑容.

點擊查看圖片來源

等等! 我發現了什麼!

image-20220818172037979

這是個女孩子的名字?! 不對, 這不是重點, 重點是這樣的寫法居然是17個月前提交的? 如果説我剛剛+哈希路由的方式是正確的解法, 那麼這個bug已經存在了17個月了!? 我繼續翻閲着源代碼. 發現了令人細思恐極的細節.

image-20220818172518053

在19個月前, 同樣有人使用了不帶哈希值的路由寫法. 為什麼明明不正確的寫法, 那麼多人要用呢? 難道...難道他們在代碼中下毒? 不對! 我唯一能想到的可能, 那就是他們的寫法在當初是正確的!

想到這, 我的笑容凝固了. 所以我們來複盤一下. 如果一個路由鏈接dual-axes/dual-line, 缺少了哈希那一部分, 那麼作者希望的正常表現是啥樣的呢? 再看一下文檔的描述

image-20220818173626437

image-20220818175344026

第一個demo鏈接是dual-axes/dual-line, 作者只是想表達這裏是雙軸折線圖, 至於具體是哪種雙軸折線, 重要嗎? 不重要. 因此沒必要指定哈希參數, 也就是具體的demo. 而第二個demo鏈接是dual-axes/column-line原理同上.

img

通過以上分析, 我們可以確定, 不帶哈希參數的鏈接是合法的! 他默認應該定位二級菜單的第一個demo. 只是這個功能在後來的迭代中被改壞了.

你以為的真相只是你以為

按理説, 好不容易發現了真相, 我應該是無比的興奮與開心的. 事實上, 確實很開心, 但是開心中又夾雜着抑鬱. 開心是因為發現了更深層的原因, 而抑鬱的點是"我咋知道他路由跳轉是咋弄的? G2Plot又不是我開發的!"

我像剛剛獎勵完自己一樣呆坐在電腦前, 不知所措. 好不容易挖掘到的線索就此中斷了. 突然間, 我的腦海中閃過幾個antv產品的模樣.

image-20220818180541440

image-20220818180844867

image-20220818180854929

這幾個antv產品, 不能説十分相似, 只能説一模一樣. 等等! 一模一樣? 難道説...又一個猜想浮現在我的腦海中: 這些網站的構建是不是用的同一套系統? 那麼問題會不會和G2Plot沒有半毛錢關係, 而是這個構建系統上?

想到這, 我迫不及待地打開了X6(螞蟻金服的圖編輯器, 反正也是個可視化項目), 隨便點開了個示例, 觀察他的URL結構.

image-20220818181630457

好! 很好!! 非常好!!! 這個鏈接和我們前面的案例一樣, 表現一切正常! 我屏住呼吸, 把哈希值去掉, 按下回車!

挖槽! X6也有這個問題! 這證明我的猜想沒錯! 他們師出同門, 一定是公共的網站構建體系出了問題! 事情變得越來越有趣了! 我們理一下思路, 從一開始的文檔錯誤的方向一路推理到現在.

image-20220818190420931

我擼起了袖子, 準備大幹一場. 今天, 老夫就算拿出全部的本領, 掘地三尺也要把這個bug挖出來!

點擊查看圖片來源

那麼這個神秘的公共構建體系, 到底是在哪呢? 確實沒什麼想法, 但是不管他在哪裏, 他總歸要體現在運行時吧? 接下來, 我clone了G2Plot的倉庫, 簡單看了下項目結構, 這些目錄一看就知道是幹嘛的, 挺不錯.

image-20220818191126231

裝好了依賴, 念出了咒語.

npm run start

接下來咱們故技重施, 看看效果.

還是原來的配方, 還是熟悉的味道. 只不過這次訪問的是迷你圖而不是面積圖了. 再看一眼倉庫文件

image-20220818194107039

哦豁! 無中生有! 一眼就看到了public目錄, 我在裏面一頓搜尋. 發現只有一個看起來像雙軸圖的東西. 大幾十個圖表, 為什麼只有雙軸圖呢? 難道是因為我剛剛訪問過?

image-20220818201643105

於是我隨便點了個折線圖, 再觀察下目錄

image-20220818201819573

可以確定, 當我訪問哪個類別的時候, 他就會生成該類別的page-data.json, 那麼這到底是何方神聖呢? 我們打開dual-line/page-data.json, 看看他到底賣的什麼藥. 打開這個文件, 我們對其格式化後, 隨便翻翻, 就能找到一個叫allDemos的東西. 通過名字可以判斷, 好像是所有demo的集合.

image-20220818202008180

再隨便翻翻, 發現還有個exampleSections的東西, 看起來好像是該類別的所有demo.

image-20220818202137889

等等! 再看一眼本地開發模式下的錯誤重定向頁面

image-20220818201425766

這個demo不就是allDemos裏的第一個demo嗎? 而他應該導向的地址, 不就是exampleSections.examples裏的第一個demo嗎? Soga! 那麼我們可以推斷, 當路由不帶哈希值時, 構建系統應該讀的是exampleSections.examples[0], 而不是allDemos[0]

有意思! 太有意思了! 現在我們來更新下咱們的破案手冊.

image-20220818203008048

鎖定目標, 精準打擊!

接下來, 我們得找找到底這個公共構建體系在什麼地方? 我們翻看G2Plotpackage.json, 看看有沒有啥線索.

"devDependencies": {
    "@antv/data-set": "^0.11.5",
    "@antv/gatsby-theme-antv": "^1.1.15",
    "@babel/core": "^7.10.4",
    "@babel/plugin-transform-runtime": "^7.11.5",
    "@babel/preset-env": "^7.10.4",
    "@babel/runtime": "^7.11.2",
    "@commitlint/cli": "^8.2.0",
    "@commitlint/config-angular": "^8.2.0",
    "@types/jest": "^25.2.1",
    "@typescript-eslint/eslint-plugin": "^2.0.0",
    "@typescript-eslint/parser": "^2.0.0",
    "all-contributors-cli": "^6.20.0",
    "antd": "^4.8.4",
    "babel-loader": "^8.1.0",
    "chroma-js": "^2.1.2",
    "conventional-changelog-cli": "^2.0.34",
    "cross-env": "^7.0.2",
    "eslint": "^6.1.0",
    "eslint-config-prettier": "^6.0.0",
    "eslint-plugin-import": "^2.22.0",
    "eslint-plugin-prettier": "^3.1.0",
    "gatsby": "^2.24.63",
    "gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.27",
    "generate-changelog": "^1.8.0",
    "gh-pages": "^3.1.0",
    "husky": "^4.2.3",
    "jest": "^26.0.1",
    "jest-electron": "^0.1.7",
    "jest-extended": "^0.11.2",
    "jest-matcher-deep-close-to": "^2.0.1",
    "limit-size": "^0.1.3",
    "lint-md-cli": "^0.1.2",
    "lint-staged": "^10.0.7",
    "miz": "^1.0.1",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.0.1",
    "rc-for-plots": "^0.0.1",
    "react": "^16.11.0",
    "react-dom": "^16.11.0",
    "react-i18next": "^11.7.0",
    "rimraf": "^3.0.0",
    "ts-jest": "^25.4.0",
    "ts-loader": "^7.0.0",
    "typescript": "^3.5.3",
    "webpack": "^4.44.2",
    "webpack-bundle-analyzer": "^3.9.0",
    "webpack-cli": "^3.3.7",
    "webpack-dev-server": "^3.9.0"
  },

雖然密密麻麻的, 但是這裏面絕大部分其實都是眼熟的. 咱們想一想, 這套構建體系可能是螞蟻金服以外的人寫的嗎? 完全沒有可能! 第一, 這個難度非常大, 一定要非常瞭解他們的需求才行. 第二, 我們都知道做開源是用愛發電的. 但是這樣龐大的構建體系需要的電量極大, 如果不是拿着工資, 我想電量是不足以支撐的. 那麼既然是螞蟻的人寫的, 那這個倉庫最有可能是放在@antv下面的. 這樣想來, 那麼最有可能的就是下面這個了.

"@antv/gatsby-theme-antv": "^1.1.15",

然後我們去github上搜一下這個倉庫

image-20220818204206205

不僅可以搜到, 還不打自招了. 通過readme部分, 我們完全確認了這個貨就是始作俑者! 接下來又開始頭疼了, 我怎麼調試呢? 要知道, G2並沒有使用monorepo的方式去管理, 關於G2的架構, 我在之前的一篇文章中簡單描述過, 在這裏我就不贅述了. 感興趣的可以訪問: 使用antv/G2生態半年有感

在多倉庫管理模式下, 也不是沒有辦法調試, 我們可以藉助yalc. 但是我們都知道, 項目依賴是個神奇的東西, 坦白説, 把G2Plot跑起來就花了倆小時. 而這個構建系統gatsby-theme-antv我本地則是完全跑不起來.

image-20220818212456091

什麼? 你問我是什麼開發環境? 既然你誠心誠意的問了, 那我就大發慈悲地告訴你吧.

image-20220818233653468

論配置, 誰能與之一戰? 但就是跑不起來. 似乎線索又斷了...怎麼辦呢? 既然我們能把G2Plot跑起來, 要不然我們去G2Plotnode_modules裏看看?

image-20220818234156565

可以看到, 這個gatsby-theme-antv也是比較簡潔的目錄, 我們想要的東西應該就在這個裏面. 我的預感告訴我, 我想要的東西大概率就在components文件夾裏, 於是我就點開隨便看了看

image-20220818234456998

坦白説, 我不是這個項目的開發者, 我對這個項目是完全沒概念的, 這真的非常艱難. 就算核心bug在某一個文件裏, 對我來説也和大海撈針差不多了. 其實之前我們在分析的時候也數次遇到束手無策的情況. 每當這個時候, 我們要嘗試轉換思路, 不要在一棵樹上吊死. 不管他的文件結構有多複雜, 最終不都得打包到一起嗎? 所以我打開了G2Plot的控制枱, 找到了commons.js, 我們想要找的東西, 一定在這49萬行的代碼中...

image-20220818235236870

嗯, 49萬行代碼. 更絕望了...

嚶嚶嚶

於是, 我又去讀node_modules裏的源碼了, 碰碰運氣, 隨便點幾個文件到處看看, 邊看邊分析依賴關係和渲染邏輯. 於是, 我終於看到了下面這段代碼:

image-20220819000132914

其實我不是隨便點開文件看的, 我是通過在common.js中打斷點的方式找到這個地方的, 但是這個過程充滿了試探與分析, 而且會在多個組件中跳來跳去, 又繞又繁瑣. 所以就一筆帶過吧~

兄弟們! 當我看見這段代碼的時候, 比看見黑絲都興奮吶! 這currentExample是啥? 從語義上來説, 就是當前展示的demo. 那麼現在的問題不就是URL沒有帶哈希值的時候導致currentExample不對嗎? 當然, 這只是我的猜想. 還需要更進一步的證據. 此時, 我利用react的dev-tool來查找這個PlayGrounds組件, 看看是不是渲染的部分.

image-20220819000806731

哎, 只是個菜單組件啊? 不是渲染區域的組件啊...空歡喜一場. 等等! 就算渲染區域想正確渲染, 不也得保證左側菜單選到正確的demo嗎? 所以是不是可以理解為, 就是因為左側菜單沒有選到正確的demo, 才導致的渲染區域不是正確的demo? 好! 證據確鑿, 嚴查PlayGrounds組件!

但稍加思考後我就確定了, PlayGrounds只是一個傀儡罷了, 他是接收currentExample的, 並沒有權利決定currentExample是誰. 因此我們應該把矛頭對準調用這個組件的地方.

image-20220819001510566

這是真假美猴王嗎? 區別就是有沒有s? 不糾結了, 那麼我們就開始探尋究竟在PlayGround組件中, 是如何定義currentExample的.

image-20220819002541277

首先我們找到這個組件定義的地方, 看到他接收的參數包含了exampleSectionsallDemos. 很好! 非常好! 不出意外的話, 他會在某一個地方, 取allDemos的第一個元素, 所以我們在這個組件中搜一下allDemos[0]看看能不能搜到. 其實是搜不到的, 但是搜[0]卻可以搜到.

image-20220819001842567

其實這裏的examples就是allDemos, 為什麼這麼説呢, 我們看看調用Playground的地方是如何傳參的

image-20220819005305657

看着這命名, 我悟了! 這波啊, 我願稱之為最強幻術! 其實通過打斷點的方式, 也能確定這裏的examples就是我們一開始看到的allDemos

image-20220819005533355

接下來就好辦了! 只要讓讀取的默認值取自exampleSections.examples即可!

image-20220822170948982

然後我們保存後, G2Plot會重新觸發HMR, 再試一下看看是否正常了

demo5

果不其然! 接下來就是給antv提PR了. 老夫花了一天的時間費了九牛二虎之力, 就改了2行代碼...

<img src="https://eve-sama.oss-cn-shanghai.aliyuncs.com/blog/202208190103248.png" style="zoom:50%;" />

真正的真相, 不得而知

突然, 我很好奇, 到底是什麼原因, 導致這樣的bug存在呢? 於是我通過git log查找到上一次更改這一行的人.

image-20220819102309727

顯然, 不是這次的提交導致的, 她只是優化了下寫法, 我又往前翻了幾十個commits, 發現其實從一開始, 就是examples[0]的寫法.

image-20220819102746240

看來, 我又錯了...看着自己推演的過程, 我不禁開始思考, 問題到底出在哪?

image-20220819103530733

我唯一能想到的, 就是構建系統的人, 一開始就是希望強制+上哈希路由的. 而後面寫文檔的人, 則是對此並不知情, 在寫完文檔後也沒有去檢查效果. 不知道這個推斷是否正確, 但其實不重要了. 我的PR使得這種寫法變得合法.

結語

其實這個過程還蠻有趣的. 從一開始以為是文檔錯誤, 到化身偵探一路推理排查找到關鍵點. 其實這個和工作內容很像, 很多問題浮現出來的都是表面問題, 而解決表面問題其實並沒有什麼難度, 對症下藥即可. 但是想要藥到病除, 那是不現實的. 唯有多加思考、 推理、 分析, 找到核心問題所在, 從根本上解決問題才是一勞永逸的. 當然, 二者的成本差距也非常大. 依據實際情況抉擇即可.


我是前夕, 專注於前端和成長, 希望我的內容可以幫助到你. 公眾號: 前夕小課堂

image-20240403101717261

本文禁止轉載!

Add a new 評論

Some HTML is okay.