前言

什么? 你不了解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行代码...

真正的真相, 不得而知

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

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

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

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

结语

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


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

本文禁止转载!

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

  1. 2019 蚂蚁金服java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.蚂蚁金服等公司offer,岗位是Java后端开发,因为发展原因最终选择去了蚂蚁金服,入职一年时间了,也成为了面 ...

  2. 我是如何拿到蚂蚁金服 offer 的 ?

    阅读本文大概需要 5.6 分钟. 作者:翟洪毅 一.梦想和被拒 二.积累 三.结语   首先介绍一下投稿作者 翟洪毅,16年华理计算机本科毕业.在年前拿到了蚂蚁金服Java开发的offer,P6. 工 ...

  3. 蚂蚁金服新一代数据可视化引擎 G2

    新公司已经呆了一个多月,目前着手一个数据可视化的项目,数据可视化肯定要用到图形库如D3.Highcharts.ECharts.Chart等,经决定我的这个项目用阿里旗下蚂蚁金服所开发的G2图表库. 官 ...

  4. 蚂蚁金服缘何自研Service Mesh?

    2018年,微服务方兴未艾,Service Mesh(服务网格)又快速崛起.有观点认为,2018年可被称之为“Service Mesh元年”,在未来两年中,Service Mesh将迎来爆发式增长,成 ...

  5. 蚂蚁金服研发的金融级分布式中间件SOFA背后的故事

    导读:GIAC大会期间,蚂蚁金服杨冰,黄挺等讲师面向华南技术社区做了<数字金融时代的云原生架构转型路径>和<从传统服务化走向Service Mesh>等演讲,就此机会,高可用架 ...

  6. 蚂蚁金服SOFAMesh在多语言上的实践

    在用一项技术前,一定要知道它的优点和缺点,它的优点是否对你有足够的吸引力,它的缺点不足你是否有办法补上.黄挺在CNUTCon全球运维大会上的分享也很不错. 黄挺,蚂蚁金服高级技术专家,蚂蚁金服分布式架 ...

  7. 蚂蚁金服 Service Mesh 实践探索

    SOFAMesh是蚂蚁金服在ServiceMesh方向上的探索,下面是它高级技术专家敖小剑在QCon上海2018上的演讲. Service Mesh 是一个 基础设施层,用于处理服务间通讯.现代云原生 ...

  8. 天下代码一大抄,整个案例的搬是什么鬼!疑似冒充蚂蚁金服高级Java开发工程师?你大爷

    写在开始 上班前的第一件事,就是码云看看有什么消息,回复下网友的问题.如果看到喜欢的项目会点进去瞅瞅,然后就开始一天的工作. 然而,这一天的工作并不开心,一个今日热门项目让自己很恼火,一开始感觉并没有 ...

  9. 最新 蚂蚁金服java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.蚂蚁金服等10家互联网公司的校招Offer,因为某些自身原因最终选择了蚂蚁金服.6.7月主要是做系统复习.项目复盘.Leet ...

  10. 胡喜:从 BASIC 到 basic ,蚂蚁金服技术要解决两个基本的计算问题

    摘要: 揭开 BASIC College 神秘面纱,蚂蚁金服首次揭秘人才培养机制. 导读:5 月 6 日,蚂蚁金服副 CTO 胡喜在 2019 年 QCon 上做了<蚂蚁金服十五年技术架构演进之 ...

随机推荐

  1. (二)Git 学习之基础篇

    一.理论基础 1.1 Git 记录的是什么? Git 和其它版本控制系统(如 SVN)的主要差别在于 Git 对待数据的方式. 1.1.1 SVN 记录差异比较 从概念上来说,SVN 以文件变更列表的 ...

  2. Abp.Zero 手机号免密登录验证与号码绑定功能的实现(二):改造Abp默认实现

    接下来我们重写原Abp的部分实现,来驳接手机号相关业务. 改造User类 重写PhoneNumber使得电话号码为必填项,和中国大陆手机号11位长度 public new const int MaxP ...

  3. electron vite2 vue3 安装 cvep my-electron-cvep

    npm config set registry=https://registry.npm.taobao.org/ npm config set ELECTRON_MIRROR=http://npm.t ...

  4. PE文件手工压缩

    序 本文要压缩的PE文件来自软件漏洞这门课上布置的作业,代码逻辑很简单,直接运行就能看出来,就是调库来弹两个对话窗口.笔者主要记录一下对这个文件的分析和一步步实现手工压缩的过程.在此提供原文件的下载方 ...

  5. Android 开发Day8

    /* AUTO-GENERATED FILE. DO NOT MODIFY. * * This class was automatically generated by the * gradle pl ...

  6. JS原生2048小游戏源码分享

    最近在学习算法方面的知识,看到了一个由算法主导的小游戏,这里给大家分享下代码: 效果: 代码: <head> <meta charset="UTF-8"> ...

  7. uni-app 地图全解析+事件监听

    最近找到了一篇uni-app的地图解决方案精品文章,这里分享给大家,希望对大家有所帮助 转载地址:https://blog.csdn.net/cplvfx/article/details/111447 ...

  8. C++ pieces

    standard lib fmax double fmax (double x, double y); float fmax (float x, float y); long double fmax ...

  9. 基于R语言的GD库实现地理探测器并自动将连续变量转为类别变量

      本文介绍基于R语言中的GD包,依据栅格影像数据,实现自变量最优离散化方法选取与执行,并进行地理探测器(Geodetector)操作的方法.   首先,在R语言中进行地理探测器操作,可通过geode ...

  10. 百度文库内容复制 C# webbrowser+Nsoup

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...