写点什么

给蚂蚁金服 antv 提个 PR, 以为是改个错别字, 未曾想背后的原因竟如此复杂!

作者:前夕
  • 2024-04-03
    上海
  • 本文字数:5364 字

    阅读完需:约 18 分钟

前言

什么? 你不了解 G2Plot? 没关系, 今天咱们要分享的内容和 G2Plot 的关系, 就像雷锋和雷峰塔的关系. 因此, 不必担心听不懂. 我一直觉得, 如果我写的文章有人看不懂, 那一定是我写的不够好. 而不是读者的问题.

业务背景

最近在使用G2Plot双轴图开发业务. 大概长下面这个样子.



既然要开发双轴图, 那么先看一遍文档是正常操作. 于是我就打开了他的官网, 看到了这样的一段话, 以及 demo 链接.



看完后似懂非懂, 这不有 DEMO 吗? 那我们就点击看看效果.



兄弟们! 好像不太对啊. 虽然我才刚刚开始看双轴图, 对它的业务不是特别了解. 但是我不瞎啊. 这标题不是写着面积图吗? 而且左侧分类也属于面积图. 好家伙, 在我眼皮底下狸猫换太子?


显然, 这是路由跳转有问题. 要不然我就提个 PR 帮他们改了吧. 这种链接错误、错别字的 PR, 本来我是不屑提的. 但是不知道为什么, 冥冥之中就觉得这个 PR 和我八字相克, 那就不好意思了, 老夫今天就替天行道了!


找到链接错误之处

那么既然要提 PR, 首先我得知道这个正确的链接应该是啥样的. 于是我观察了下面积图的第一个 demo 的链接



显然, 路由里面主要有 3 个部分area/basic#basic, area/basic其实大家都好理解, 为什么最后还要+个哈希参数呢? 简单思考后很容易想到, 因为basic下面有好多的例子, 不同的 demo 对应不同的哈希. 我们戳第二个 demo 试试看.



果然! 只有 #后面的东西变化了. 接下来我们找到一开始时, 我们点击文档跳转的链接https://g2plot.antv.vision/zh/examples/dual-axes/dual-line显然, 就是少了哈希参数. 我们为其+上小尾巴应该就可以正常访问了.



大功告成! 接下来我们要做的就是找到源码中的跳转之处替换链接即可.


事情好像没有那么简单


打开 github, clone 仓库后, 搜索结果, 一气呵成. 接下来我要做的事情就是替换这个 markdown 中的地址. 一想到就这样轻轻松松的解决了一个 bug, 我露出了邪恶的笑容.



等等! 我发现了什么!



这是个女孩子的名字?! 不对, 这不是重点, 重点是这样的写法居然是 17 个月前提交的? 如果说我刚刚+哈希路由的方式是正确的解法, 那么这个 bug 已经存在了 17 个月了!? 我继续翻阅着源代码. 发现了令人细思恐极的细节.



在 19 个月前, 同样有人使用了不带哈希值的路由写法. 为什么明明不正确的写法, 那么多人要用呢? 难道...难道他们在代码中下毒? 不对! 我唯一能想到的可能, 那就是他们的写法在当初是正确的!


想到这, 我的笑容凝固了. 所以我们来复盘一下. 如果一个路由链接dual-axes/dual-line, 缺少了哈希那一部分, 那么作者希望的正常表现是啥样的呢? 再看一下文档的描述




第一个 demo 链接是dual-axes/dual-line, 作者只是想表达这里是双轴折线图, 至于具体是哪种双轴折线, 重要吗? 不重要. 因此没必要指定哈希参数, 也就是具体的 demo. 而第二个 demo 链接是dual-axes/column-line原理同上.



通过以上分析, 我们可以确定, 不带哈希参数的链接是合法的! 他默认应该定位二级菜单的第一个 demo. 只是这个功能在后来的迭代中被改坏了.

你以为的真相只是你以为

按理说, 好不容易发现了真相, 我应该是无比的兴奋与开心的. 事实上, 确实很开心, 但是开心中又夹杂着抑郁. 开心是因为发现了更深层的原因, 而抑郁的点是**"我咋知道他路由跳转是咋弄的? G2Plot 又不是我开发的!"**


我像刚刚奖励完自己一样呆坐在电脑前, 不知所措. 好不容易挖掘到的线索就此中断了. 突然间, 我的脑海中闪过几个 antv 产品的模样.





这几个 antv 产品, 不能说十分相似, 只能说一模一样. 等等! 一模一样? 难道说...又一个猜想浮现在我的脑海中: 这些网站的构建是不是用的同一套系统? 那么问题会不会和 G2Plot 没有半毛钱关系, 而是这个构建系统上?


想到这, 我迫不及待地打开了 X6(蚂蚁金服的图编辑器, 反正也是个可视化项目), 随便点开了个示例, 观察他的 URL 结构.



好! 很好!! 非常好!!! 这个链接和我们前面的案例一样, 表现一切正常! 我屏住呼吸, 把哈希值去掉, 按下回车!



挖槽! X6 也有这个问题! 这证明我的猜想没错! 他们师出同门, 一定是公共的网站构建体系出了问题! 事情变得越来越有趣了! 我们理一下思路, 从一开始的文档错误的方向一路推理到现在.



我撸起了袖子, 准备大干一场. 今天, 老夫就算拿出全部的本领, 掘地三尺也要把这个 bug 挖出来!



那么这个神秘的公共构建体系, 到底是在哪呢? 确实没什么想法, 但是不管他在哪里, 他总归要体现在运行时吧? 接下来, 我 clone 了G2Plot的仓库, 简单看了下项目结构, 这些目录一看就知道是干嘛的, 挺不错.



装好了依赖, 念出了咒语.


npm run start
复制代码


接下来咱们故技重施, 看看效果.



还是原来的配方, 还是熟悉的味道. 只不过这次访问的是迷你图而不是面积图了. 再看一眼仓库文件



哦豁! 无中生有! 一眼就看到了public目录, 我在里面一顿搜寻. 发现只有一个看起来像双轴图的东西. 大几十个图表, 为什么只有双轴图呢? 难道是因为我刚刚访问过?



于是我随便点了个折线图, 再观察下目录



可以确定, 当我访问哪个类别的时候, 他就会生成该类别的page-data.json, 那么这到底是何方神圣呢? 我们打开dual-line/page-data.json, 看看他到底卖的什么药. 打开这个文件, 我们对其格式化后, 随便翻翻, 就能找到一个叫allDemos的东西. 通过名字可以判断, 好像是所有 demo 的集合.



再随便翻翻, 发现还有个exampleSections的东西, 看起来好像是该类别的所有 demo.



等等! 再看一眼本地开发模式下的错误重定向页面



这个 demo 不就是allDemos里的第一个 demo 吗? 而他应该导向的地址, 不就是exampleSections.examples里的第一个 demo 吗? Soga! 那么我们可以推断, 当路由不带哈希值时, 构建系统应该读的是exampleSections.examples[0], 而不是allDemos[0]


有意思! 太有意思了! 现在我们来更新下咱们的破案手册.


锁定目标, 精准打击!

接下来, 我们得找找到底这个公共构建体系在什么地方? 我们翻看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 上搜一下这个仓库



不仅可以搜到, 还不打自招了. 通过readme部分, 我们完全确认了这个货就是始作俑者! 接下来又开始头疼了, 我怎么调试呢? 要知道, G2 并没有使用 monorepo 的方式去管理, 关于 G2 的架构, 我在之前的一篇文章中简单描述过, 在这里我就不赘述了. 感兴趣的可以访问: 使用antv/G2生态半年有感


在多仓库管理模式下, 也不是没有办法调试, 我们可以借助yalc. 但是我们都知道, 项目依赖是个神奇的东西, 坦白说, 把G2Plot跑起来就花了俩小时. 而这个构建系统gatsby-theme-antv我本地则是完全跑不起来.



什么? 你问我是什么开发环境? 既然你诚心诚意的问了, 那我就大发慈悲地告诉你吧.



论配置, 谁能与之一战? 但就是跑不起来. 似乎线索又断了...怎么办呢? 既然我们能把G2Plot跑起来, 要不然我们去G2Plotnode_modules里看看?



可以看到, 这个gatsby-theme-antv也是比较简洁的目录, 我们想要的东西应该就在这个里面. 我的预感告诉我, 我想要的东西大概率就在components文件夹里, 于是我就点开随便看了看



坦白说, 我不是这个项目的开发者, 我对这个项目是完全没概念的, 这真的非常艰难. 就算核心 bug 在某一个文件里, 对我来说也和大海捞针差不多了. 其实之前我们在分析的时候也数次遇到束手无策的情况. 每当这个时候, 我们要尝试转换思路, 不要在一棵树上吊死. 不管他的文件结构有多复杂, 最终不都得打包到一起吗? 所以我打开了G2Plot的控制台, 找到了commons.js, 我们想要找的东西, 一定在这 49 万行的代码中...



嗯, 49 万行代码. 更绝望了...



于是, 我又去读 node_modules 里的源码了, 碰碰运气, 随便点几个文件到处看看, 边看边分析依赖关系和渲染逻辑. 于是, 我终于看到了下面这段代码:



其实我不是随便点开文件看的, 我是通过在common.js中打断点的方式找到这个地方的, 但是这个过程充满了试探与分析, 而且会在多个组件中跳来跳去, 又绕又繁琐. 所以就一笔带过吧~


兄弟们! 当我看见这段代码的时候, 比看见黑丝都兴奋呐! 这currentExample是啥? 从语义上来说, 就是当前展示的 demo. 那么现在的问题不就是 URL 没有带哈希值的时候导致currentExample不对吗? 当然, 这只是我的猜想. 还需要更进一步的证据. 此时, 我利用 react 的 dev-tool 来查找这个PlayGrounds组件, 看看是不是渲染的部分.



哎, 只是个菜单组件啊? 不是渲染区域的组件啊...空欢喜一场. 等等! 就算渲染区域想正确渲染, 不也得保证左侧菜单选到正确的 demo 吗? 所以是不是可以理解为, 就是因为左侧菜单没有选到正确的 demo, 才导致的渲染区域不是正确的 demo? 好! 证据确凿, 严查PlayGrounds组件!


但稍加思考后我就确定了, PlayGrounds只是一个傀儡罢了, 他是接收currentExample的, 并没有权利决定currentExample是谁. 因此我们应该把矛头对准调用这个组件的地方.



这是真假美猴王吗? 区别就是有没有s? 不纠结了, 那么我们就开始探寻究竟在PlayGround组件中, 是如何定义currentExample的.



首先我们找到这个组件定义的地方, 看到他接收的参数包含了exampleSectionsallDemos. 很好! 非常好! 不出意外的话, 他会在某一个地方, 取allDemos的第一个元素, 所以我们在这个组件中搜一下allDemos[0]看看能不能搜到. 其实是搜不到的, 但是搜[0]却可以搜到.



其实这里的examples就是allDemos, 为什么这么说呢, 我们看看调用Playground的地方是如何传参的



看着这命名, 我悟了! 这波啊, 我愿称之为最强幻术! 其实通过打断点的方式, 也能确定这里的examples就是我们一开始看到的allDemos



接下来就好办了! 只要让读取的默认值取自exampleSections.examples即可!



然后我们保存后, G2Plot会重新触发HMR, 再试一下看看是否正常了



果不其然! 接下来就是给 antv 提 PR 了. 老夫花了一天的时间费了九牛二虎之力, 就改了 2 行代码...


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

真正的真相, 不得而知

突然, 我很好奇, 到底是什么原因, 导致这样的 bug 存在呢? 于是我通过 git log 查找到上一次更改这一行的人.



显然, 不是这次的提交导致的, 她只是优化了下写法, 我又往前翻了几十个 commits, 发现其实从一开始, 就是examples[0]的写法.



看来, 我又错了...看着自己推演的过程, 我不禁开始思考, 问题到底出在哪?



我唯一能想到的, 就是构建系统的人, 一开始就是希望强制+上哈希路由的. 而后面写文档的人, 则是对此并不知情, 在写完文档后也没有去检查效果. 不知道这个推断是否正确, 但其实不重要了. 我的 PR 使得这种写法变得合法.

结语

其实这个过程还蛮有趣的. 从一开始以为是文档错误, 到化身侦探一路推理排查找到关键点. 其实这个和工作内容很像, 很多问题浮现出来的都是表面问题, 而解决表面问题其实并没有什么难度, 对症下药即可. 但是想要药到病除, 那是不现实的. 唯有多加思考、 推理、 分析, 找到核心问题所在, 从根本上解决问题才是一劳永逸的. 当然, 二者的成本差距也非常大. 依据实际情况抉择即可.




我是前夕, 专注于前端和成长, 希望我的内容可以帮助到你. 公众号: 前夕小课堂



本文禁止转载!

发布于: 刚刚阅读数: 3
用户头像

前夕

关注

专注于前端和成长. 公众号: 前夕小课堂 2019-01-15 加入

还未添加个人简介

评论

发布
暂无评论
给蚂蚁金服antv提个PR, 以为是改个错别字, 未曾想背后的原因竟如此复杂!_前端_前夕_InfoQ写作社区