给蚂蚁金服 antv 提个 PR, 以为是改个错别字, 未曾想背后的原因竟如此复杂!
前言
什么? 你不了解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]
有意思! 太有意思了! 现在我们来更新下咱们的破案手册.
锁定目标, 精准打击!
接下来, 我们得找找到底这个公共构建体系在什么地方? 我们翻看G2Plot
的package.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
跑起来, 要不然我们去G2Plot
的node_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
的.
首先我们找到这个组件定义的地方, 看到他接收的参数包含了exampleSections
和allDemos
. 很好! 非常好! 不出意外的话, 他会在某一个地方, 取allDemos
的第一个元素, 所以我们在这个组件中搜一下allDemos[0]
看看能不能搜到. 其实是搜不到的, 但是搜[0]
却可以搜到.
其实这里的examples
就是allDemos
, 为什么这么说呢, 我们看看调用Playground
的地方是如何传参的
看着这命名, 我悟了! 这波啊, 我愿称之为最强幻术! 其实通过打断点的方式, 也能确定这里的examples
就是我们一开始看到的allDemos
接下来就好办了! 只要让读取的默认值取自exampleSections.examples
即可!
然后我们保存后, G2Plot
会重新触发HMR
, 再试一下看看是否正常了
果不其然! 接下来就是给antv提PR了. 老夫花了一天的时间费了九牛二虎之力, 就改了2行代码...
真正的真相, 不得而知
突然, 我很好奇, 到底是什么原因, 导致这样的bug存在呢? 于是我通过git log查找到上一次更改这一行的人.
显然, 不是这次的提交导致的, 她只是优化了下写法, 我又往前翻了几十个commits, 发现其实从一开始, 就是examples[0]
的写法.
看来, 我又错了...看着自己推演的过程, 我不禁开始思考, 问题到底出在哪?
我唯一能想到的, 就是构建系统的人, 一开始就是希望强制+上哈希路由的. 而后面写文档的人, 则是对此并不知情, 在写完文档后也没有去检查效果. 不知道这个推断是否正确, 但其实不重要了. 我的PR使得这种写法变得合法.
结语
其实这个过程还蛮有趣的. 从一开始以为是文档错误, 到化身侦探一路推理排查找到关键点. 其实这个和工作内容很像, 很多问题浮现出来的都是表面问题, 而解决表面问题其实并没有什么难度, 对症下药即可. 但是想要药到病除, 那是不现实的. 唯有多加思考、 推理、 分析, 找到核心问题所在, 从根本上解决问题才是一劳永逸的. 当然, 二者的成本差距也非常大. 依据实际情况抉择即可.
我是前夕, 专注于前端和成长, 希望我的内容可以帮助到你. 公众号: 前夕小课堂
本文禁止转载!
给蚂蚁金服 antv 提个 PR, 以为是改个错别字, 未曾想背后的原因竟如此复杂!的更多相关文章
- 2019 蚂蚁金服java面试笔试题 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.蚂蚁金服等公司offer,岗位是Java后端开发,因为发展原因最终选择去了蚂蚁金服,入职一年时间了,也成为了面 ...
- 我是如何拿到蚂蚁金服 offer 的 ?
阅读本文大概需要 5.6 分钟. 作者:翟洪毅 一.梦想和被拒 二.积累 三.结语 首先介绍一下投稿作者 翟洪毅,16年华理计算机本科毕业.在年前拿到了蚂蚁金服Java开发的offer,P6. 工 ...
- 蚂蚁金服新一代数据可视化引擎 G2
新公司已经呆了一个多月,目前着手一个数据可视化的项目,数据可视化肯定要用到图形库如D3.Highcharts.ECharts.Chart等,经决定我的这个项目用阿里旗下蚂蚁金服所开发的G2图表库. 官 ...
- 蚂蚁金服缘何自研Service Mesh?
2018年,微服务方兴未艾,Service Mesh(服务网格)又快速崛起.有观点认为,2018年可被称之为“Service Mesh元年”,在未来两年中,Service Mesh将迎来爆发式增长,成 ...
- 蚂蚁金服研发的金融级分布式中间件SOFA背后的故事
导读:GIAC大会期间,蚂蚁金服杨冰,黄挺等讲师面向华南技术社区做了<数字金融时代的云原生架构转型路径>和<从传统服务化走向Service Mesh>等演讲,就此机会,高可用架 ...
- 蚂蚁金服SOFAMesh在多语言上的实践
在用一项技术前,一定要知道它的优点和缺点,它的优点是否对你有足够的吸引力,它的缺点不足你是否有办法补上.黄挺在CNUTCon全球运维大会上的分享也很不错. 黄挺,蚂蚁金服高级技术专家,蚂蚁金服分布式架 ...
- 蚂蚁金服 Service Mesh 实践探索
SOFAMesh是蚂蚁金服在ServiceMesh方向上的探索,下面是它高级技术专家敖小剑在QCon上海2018上的演讲. Service Mesh 是一个 基础设施层,用于处理服务间通讯.现代云原生 ...
- 天下代码一大抄,整个案例的搬是什么鬼!疑似冒充蚂蚁金服高级Java开发工程师?你大爷
写在开始 上班前的第一件事,就是码云看看有什么消息,回复下网友的问题.如果看到喜欢的项目会点进去瞅瞅,然后就开始一天的工作. 然而,这一天的工作并不开心,一个今日热门项目让自己很恼火,一开始感觉并没有 ...
- 最新 蚂蚁金服java校招面经 (含整理过的面试题大全)
从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.蚂蚁金服等10家互联网公司的校招Offer,因为某些自身原因最终选择了蚂蚁金服.6.7月主要是做系统复习.项目复盘.Leet ...
- 胡喜:从 BASIC 到 basic ,蚂蚁金服技术要解决两个基本的计算问题
摘要: 揭开 BASIC College 神秘面纱,蚂蚁金服首次揭秘人才培养机制. 导读:5 月 6 日,蚂蚁金服副 CTO 胡喜在 2019 年 QCon 上做了<蚂蚁金服十五年技术架构演进之 ...
随机推荐
- (二)Git 学习之基础篇
一.理论基础 1.1 Git 记录的是什么? Git 和其它版本控制系统(如 SVN)的主要差别在于 Git 对待数据的方式. 1.1.1 SVN 记录差异比较 从概念上来说,SVN 以文件变更列表的 ...
- Abp.Zero 手机号免密登录验证与号码绑定功能的实现(二):改造Abp默认实现
接下来我们重写原Abp的部分实现,来驳接手机号相关业务. 改造User类 重写PhoneNumber使得电话号码为必填项,和中国大陆手机号11位长度 public new const int MaxP ...
- electron vite2 vue3 安装 cvep my-electron-cvep
npm config set registry=https://registry.npm.taobao.org/ npm config set ELECTRON_MIRROR=http://npm.t ...
- PE文件手工压缩
序 本文要压缩的PE文件来自软件漏洞这门课上布置的作业,代码逻辑很简单,直接运行就能看出来,就是调库来弹两个对话窗口.笔者主要记录一下对这个文件的分析和一步步实现手工压缩的过程.在此提供原文件的下载方 ...
- Android 开发Day8
/* AUTO-GENERATED FILE. DO NOT MODIFY. * * This class was automatically generated by the * gradle pl ...
- JS原生2048小游戏源码分享
最近在学习算法方面的知识,看到了一个由算法主导的小游戏,这里给大家分享下代码: 效果: 代码: <head> <meta charset="UTF-8"> ...
- uni-app 地图全解析+事件监听
最近找到了一篇uni-app的地图解决方案精品文章,这里分享给大家,希望对大家有所帮助 转载地址:https://blog.csdn.net/cplvfx/article/details/111447 ...
- C++ pieces
standard lib fmax double fmax (double x, double y); float fmax (float x, float y); long double fmax ...
- 基于R语言的GD库实现地理探测器并自动将连续变量转为类别变量
本文介绍基于R语言中的GD包,依据栅格影像数据,实现自变量最优离散化方法选取与执行,并进行地理探测器(Geodetector)操作的方法. 首先,在R语言中进行地理探测器操作,可通过geode ...
- 百度文库内容复制 C# webbrowser+Nsoup
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...