25 岁,毕业写前端的这三年
时间回到 2016 年,彼时 996 还不算福报,比特币单价也还远远不到 1000 美元。那时的我怀着对大厂的敬畏和对前途的迷茫,拿着一纸毕业证告别了校园。这第一份名为 Web 前端开发的工作,代码写着写着就到了今天。
一晃三年过去了,从纯粹的赶需求到造轮子、做分享和带团队,许多刚毕业的自己只能仰望的事情,现在看来也并非那么遥不可及了。简单地说,我这三年中我换了三份工作,而每份工作的年终绩效都是所在部门里最高的。不过这种说法未免太功利、太乏味了。我相信这段经历并不只是周报和简历上刻板的流水账,把它在我满 25 岁之际整理下来,也许对大家来说可以是个好故事。因此也就有了这篇文章。
赶上时代的第一年
我毕业后加入的第一家公司是科大讯飞。虽然这不是家小公司,但科大讯飞对于一个科大学生来说,想入职几乎就像蓝翔学生去开挖掘机一样容易。记得面试流程大致就是和几位在讯飞身居高位的科大校友聊聊,然后就收到 offer 了。
现在的技术社区里,常常因为培训班与科班的出身之争吵个不停。但那时算是半个科班出身的我,并不觉得当时的自己除了学历,和培训班的学员有很大的区别:那时我还不清楚绝对定位和相对定位有什么差别,基本只会 jQuery 加 Bootstrap 把功能堆出来而已。不过没关系,总有不差钱的公司愿意雇 985 的同学来切图写页面,这也就是我在讯飞主要的工作内容了。
毕业的第一年里,我的工作以实现讯飞开放平台的部分 Web 前端需求为主。这期间我的精力除了实现各种业务需求,主要还是放在了对开源技术的学习和个人项目上。在我刚入职时,我所在的开发团队技术栈还是前后端不分离的 jQuery + JSP 模式,前端代码上传到静态服务器还需要靠 FTP,而团队同学还在调研 Knockout 作为下一代基础库的可能性——即便是那时,离 Knockout 诞生也有将近十年了。其实,只要能接触到社区的主流技术,许多过时的工具都是很容易被替换的。正因为如此,我很快地就应用了 Gulp 全家桶来处理一些很容易自动化的工作,并在那一年的十一假期起,开始试水现在如日中天的 Vue 2.0 和 Webpack。而后,我又搭建了内网的私有 NPM 仓库,发布了脚手架在内的十余个包来帮助大家向新的技术栈迁移。这套现在对于前端同学司空见惯的技术在那时还是颇有点新鲜的。以此为契机,我推动了团队的前后端分离实践,后来也有幸被评为了那一年的部门最佳新人。
刚毕业的那段时间,能学的和想学的东西都实在太多了。记得讯飞的内部 App 可以显示每天的打卡排名。如果你在晚上 12 点准时打卡,那么你就有机会抢到第二天的打卡第一名——这个时段的打卡截图,我的手机里存着三十多张。当然,即便讯飞确实是我呆过的公司里强制加班最多的,但其实也远远没有忙到这个量级。真相是自从那时我就发现,只要你对着一屏幕的代码又能按时交差,没人关心你到底写的是什么。借着那时候高涨的兴趣,我鼓捣出了不少现在看来基本纯属娱乐的东西。比如:
Ove Lang编程语言,可以解释执行形如(表态 (钦点 董先生 特首) (= 特首 董先生) 哦。的代码。
Merry8虚拟机,可以模拟运行用上古的 Chip8 汇编指令写出来的 PONG 游戏。
Sinomap地图库,可以用麦卡托投影算法把 GeoJSON 数据渲染到 Canvas 上。
Flylog远程调试工具,可以将其它设备页面中的 log 信息推送到 PC 端的后台上。
CSS Emoji示例,可以用两个 div 画出 Emoji 表情。
你当然可以指责那时的我只醉心技术而不管业务(这确实是某家大厂对我的面试评价),不过折腾起这些有趣的项目,让我感觉那时的生活比起在每个周末都要痛苦地赶(抄)作业的大学要自在得多。从旁观者的角度来看,我在讯飞成长得并不慢,也有许多科大校友历经多年成为了那里的技术骨干。然而事实是,我在讯飞并没有呆满一年,留下了篇《小记在讯飞的 300 天》作为纪念之后就告别了合肥。为什么离开呢?非要用一句话概括的话,应该是我感觉自己并不属于那里吧:合肥这座城市对我这个南方人来说并没有那么多归属感,而我熟悉的前端技术对于一家并非互联网公司的科技公司来说,更偏向于锦上添花而非雪中送炭。出于对地缘和个人价值的向往,我选择了告别这家食堂至今让我十分怀念的公司。感谢劲东、芳姐和家军等不少人的关照,希望有缘能够江湖再见吧。
一张调试 2016 年会大屏 Demo 的照片。那是我在讯飞最忙的 24 个小时,也是唯一一次直接和董事长汇报工作。
参与社区的第二年
我在自我介绍的时候,常常说我呆过的地方好像都是「假的」:我在科大读书,但它不在北京;我毕业前水到了鹅厂的实习,但 base 不在深圳;我离开讯飞后去了美团点评,但部门却在厦门。直到现在,我暂时也还没有离开这座城市的打算。只要不去为了那些庸俗的同龄压力去束缚自己,厦门呆起来就是个既离我的家人很近又很容易看到海的舒适城市。2017 年,美团点评的厦门研发中心在搬家前还能看到游艇,每天骑着单车上下班都能吹到海风,有时下班还能和小伙伴们去沙滩抓螃蟹 :)
我加入美团点评的面试很顺利,最主要的流程就是我把上面的一些各色玩具和相应的博客和前端老板虎哥秀了一下吧。在那里,我的主要工作是开发一个名为学城的内部知识库系统。虽然我为这个项目提交的代码占比可能已经所剩无几,但我相信只要它还活着,就总会和我有着点微妙的关系——学城的名字就是我起的。看过《权力的游戏》的同学应该都知道容纳维斯特洛大陆七国学士的 Citadel 吧,这个听起来就很有智慧的名字是不是挺适合一个知识库的呢?
对类似 Wiki 的知识库系统来说,Web 前端的富文本编辑器就非常重要了。稍有经验的同学都知道,富文本编辑是个长期以来被认为是天坑的领域,相关主流基础库从启动到成熟的时间都是以年为单位来计算的。我们显然没有必要重新发明轮子,可以基于社区已有的成熟框架来开发。在这个背景下,我首先接触了 Slate,这是一个可以让你以编写 React 组件的形式来定制自己的富文本应用的框架,它的 API 之优雅、文档之完善与源码之整洁使我很快就决定上车了,甚至都没有太在意它还属于 Beta 状态的友情提醒。
在学城的最早的几个版本中,我们基于 Slate 编写的代码还算工整。但很快问题就来了:它还不能算一个 battle-tested 的框架,我们定制的组件在编辑时暴露出了很多状态问题,bug 数量非常高。在我之前的工作经历中,对于框架用着不顺手的问题,基本都可以在业务中变通或绕过。但这对于富文本编辑器来说不适用,因为许多 bug 本身就在框架层,即便反馈到社区,也没人有义务马上替你解决。所以该怎么办呢?下载一份源码自己改吧。
修复框架的 bug 和修复业务代码的 bug,其实并没有本质的不同。毕竟只要能稳定复现,几乎所有 bug 最终都是能被修复的,只是业务代码中更容易出现肮脏的修复代码而已。但修复 bug 之后呢?在我第一年工作的时候,我在 GitHub 上还几乎没有为其他人的项目提交过代码,但我知道只要代码合并入主干分支,你就会成为这个项目的贡献者。虽然这没有任何物质奖励,但这作为贡献过开源项目的证明仍然让我十分心动。抱着这种向往的心情,我为社区提交了第一个 PR。
到现在我还记得很清楚第一个 PR 的内容:给.npmignore文件增加了一行,来解决 Babel 默认重复编译的问题。虽然只有一行代码,但出于我对开源项目的敬畏,我还是写了相当详细的描述来表达我为什么需要增加这一行代码,以及它会通过什么方式来解决问题。作者也很快就合并了这个 PR。在发现贡献开源项目原来也就是这么回事之后,我有了很大的动力将更多我的改进提交到上游。到我离开学城项目为止,我把包括 bug 修复、测试、文档在内的近 20 个 MR 合并入了 Slate 的主干,并维护了一份 0.24 版本文档的完整中文翻译。今天 Slate 已经有接近 1.5 万个 star 和超过 200 名贡献者了,而我在它的 contributors 排行榜里仍然可以排在前十。
可惜的是,即便我尽力改进 Slate,它对于表格、列表等存在嵌套的 UI 组件,其稳定性仍然难以满足学城的需求。再加上它较为激进的更新方式,我们很快就遇到了难以继续同步上游更新的问题。在充分地向公司上层抛出问题并给出备选方案之后,我们将富文本框架迁移到了架构相似但更为稳定的 ProseMirror 上,它应该已经在学城上沿用至今了。虽然 Slate 的落地时间不长,但在对它的使用与改进过程中,让我充分地理解了开源项目的运作和参与方式,我在 GitHub 上也终于不再仅仅是自娱自乐了。
2016 年和 2017 年我在 GitHub 的贡献对比。
到了第二年,我在 GitHub 上提交的代码虽然还是有不少玩票成分,但也不再是清一色的玩具了。大致有这些:
异步的数据迁移工具Bumpover,它实现了 100% 的单元测试覆盖率。
通过提取语法树节点来比较 Vue 与 Angular 相似度的naming-style-demo示例。
HTML 字符串转虚拟 DOM 的解析器html-toy-parser。
40 行的 MVC 框架nano-mvc。
在美团点评的那段时间里,除了在 GitHub 上提交代码外,我在一些技术社区里也相当活跃。记得刚入职时需要把 Vue 切换到 React,对 Vue 的怀念促使我去 SegmentFault 上回答了许多 Vue 的问题,一度是某几周内这个话题下的第一名。并且,我还发现掘金是个很适合发(新手向)前端技术文章的地方。在 2017 年结束时,我的掘金专栏已经有了 3000 以上的关注者。不过,我可不是纯粹只贡献技术正能量的傻白甜。如果那段时间你在掘金发文章讲如何深入理解 this 的四种指向和寄生混入继承之类老掉牙的糟粕内容,那么我多半会在评论区义无反顾地站出来吐槽 :) 只是现在的我已经没有兴趣参与这些口水话题了而已。
由于遇到了更适合我的机会,我也没有在美团点评工作超过一年,不过我还是很留恋刚刚入职时的那支团队。不论是晚上饭点时小伙伴们吃遍菜单的日常聚餐,还是不定期能蹭到的虎哥牌星巴克,都是相当有趣的回忆。也还要额外感谢佳立、根龙、春雨等接手学城的同学们。多亏了你们,邹老板才没有找上门来追杀我啊。
图中多边形风格的大楼就是美团点评厦门研发中心的前所在地,我们的团队曾在照片拍摄地捕获螃蟹。
突破瓶颈的第三年
在离开美团点评前,我确实可以 hold 住一些基础框架的开发了。但富文本编辑器的性质决定了它在基本稳定后的迭代方式,更多地只是修修补补而非开疆拓土。这让我感到焦虑,感觉自己处在一个为了四处救火而疲于奔命,技术进步开始放缓的瓶颈状态。在这个时候,新的机会出现了。
2017 年底的某一天,我在掘金上灌水时发现了一篇名为《我们在海边写代码》的软文,署名糖饼,看起来出自个颇有底气的前端团队。巧的是它的 base 也在厦门,并且实际地址刚好在我的上班路上。本着聊一聊反正不亏的心态,我骑着单车拜访了这家当时名叫欢乐逛的公司。
接待我的两名面试官,一位说自己花名就是糖饼(真人和头像画风不符),另一位说自己花名叫小米。他们两个人看起来蛮朴素的,但对我的长篇大论却出奇的有耐心,中途还有一名穿着黄拖鞋的男子参与了旁听。那是我经历过的最久的一轮面试,总共和我扯了应该有两三个小时。我本来以为这就是一次初面,寻思着这公司的一线同学还蛮经得住忽悠的。但其实我已经把技术面该遇到的全部 Boss 都过了一遍,没想到说好的青铜局里来了一群王者啊。
必须说,如果糖饼没有贴出他合并到 Webpack 的并行构建支持 PR,我是不会轻易选择在毕业还不到两年的时候被他怂恿着换第三次工作的。当然,我们一般的日常也并不都是那么高大上的 Rocket Science。我在这里的工作,主要集中在当时还没有独立出来的稿定设计项目里。我负责维护这个设计站点中的平面编辑器 SDK,以及相关的 UI 组件。最开始,我还以为稿定就是个较为边缘的新业务,直到公司搬家后前台都挂着稿定设计的招牌,才发现我在这大概相当于在中国邮政分拆之前进了名叫中国移动的部门。
平面设计编辑器与富文本编辑器有许多相似之处,并且这个细分领域中当前的主流开源项目,其设计思想还达不到 Slate 那种高度可扩展的灵活性,这无疑给了我很多发挥的空间。在过去的一年多的时间里,我从细小的 bug 修复开始一步步熟悉这个编辑器,终于在上个季度上线了我最想从 Slate 中借鉴的特性:组件化的可编辑元素。Slate 赋予了我们只要用 React 声明一个<Table>组件,就能在富文本编辑器中编辑表格的扩展能力。我将这个思想应用到了我们基于 Vue 的编辑器上。现在我们只需要提供基于 Vue 编写的 UI 组件,就能轻松地为编辑器组装出新的可编辑元素类型支持,而无需改动框架核心源码。再加上小伙伴在前端出图上的不懈努力,我们的编辑器做到了兼取 Canvas 与 DOM 二者之所长,目前暂时还没有主流的开源编辑器能满足这一点。
我们的平面编辑器,欢迎大家访问gaoding.com体验噢。
除了在较高的层面上,将框架按照自己的设计思想重构之外,我还在较低的细节层面上做了些有意思的工作。比如,我使用坐标系变换的思想,将图形旋转后的裁切拖拽限制算法从上百行的 if else 判断简化为了十余行数学变换;基于节点序列化数据的哈希值,实现了更细粒度的历史状态结构共享,并开源了历史状态管理库StateShot;使用CodeMod自动将 ES5 代码重构为 ES6;设计实现了编辑器的特效调节机制,并作为第一发明人提交了专利申请等等。现在我是 Web 工具团队名义上的负责人(吉祥物),工作至今写了 80 篇左右的技术博客,掘金专栏的阅读量也超过了 20 万,看起来似乎还过得去吧?
但这些工作并没有让我感觉突破了瓶颈。
很早之前我就问过小米,我们做的事情比起 Adobe,有什么特别的优势吗?小米的回答是我们需要主打内容与细分的场景,做到对普通用户更高的易用性。从商业角度来讲我很认可这个答案,但我在纯粹的技术角度上,总觉得这是更适合市场部门负责人的回答而不是属于技术部门的觉悟。并且,我也一直觉得我自己的技能体系还差了一些什么,使我虽然在一家主打商业设计而特别重视前端技术的公司里,做的事情却还不够酷。到底还差了什么呢?我的答案是渲染。
我们前端同学们所擅长编写的 JavaScript,只是在 CPU 里执行的单线程代码而已。别忘了我们还有 WebGL 这个虽然非常繁冗,但能让我们释放 GPU 潜力的武器。这个领域常常被认为是游戏开发者所专属的。而大家对于应用 Web 上的 3D 能力,首先想到的可能也都是套用 Three 等充分封装后的成熟开源渲染引擎。因此,在这个领域就没有必要重新发明轮子了吗?恰恰相反,我发现这是一片巨大的蓝海,它在 Web 设计领域的应用几乎还是白纸一张,存在着非常大的定制、优化可能性和应用前景。限于篇幅和本文的主题,这里不再详细展开,只展示一下我们自研的渲染引擎在投入开发一个月之内所得到的一些渲染效果就足够了:
在我前一段时间选择深入 WebGL 的时候,幻神提醒过我这基本相当于删号重练了。确实,在学习曲线的初期,图形学晦涩 API 的门槛让我感到非常艰难,但在坚持不使用现成引擎来实现 Demo 的一段时间后,某个时间点上我感觉自己终于可以把那些零散的点连接起来了。而这时再加上前端框架的设计经验,我确实找到了非常适合我们应用场景的一个自研方向,具体的内容暂时没有办法在这里详述,只能说我和小伙伴们正在紧张的开发中,非常希望能让我们的新特性早日和大家见面 :)
在 25 岁的今天,我感觉终于走出了之前几年在 CPU 上编写逻辑的瓶颈。从最早的玩具编程语言和游戏模拟器,再到现在的渲染引擎,所谓「编程语言、操作系统和计算机图形学」的程序员三大浪漫,我也能吹牛说自己都略有涉猎了。对我来说,从事一份工作三年之后仍然保持高涨的热情和找到值得继续钻研的技术领域,可能不是件很容易的事,因此现在的状态对我来说已经很好了。当然,要想在未来在技术上还能够继续深入,所需要的应该就不仅仅是传统的 Web 前端的领域知识,还需要更多跨领域和学科的知识了。感谢稿定这里钻研游戏引擎和图形学的大佬们,和他们的交流让我获益匪浅。现在我实在有太多需要做的事情了,因此我的博客和专栏也可能不能再维持月更的节奏,希望日后能用更多的干货来弥补 :D
后记与致谢
从第一年编写展示页和后台业务逻辑,到第二年活跃在开源社区,再到第三年开发自研框架并尝试往图形学领域转型,毕业以来的这些经历让我觉得我确实还在成长。我虽然已经不再是团队里最年轻的成员了,但现在我还远远没有到转型完全的管理者去分配需求和任务的时候:还有这么多有趣的代码可以写,放弃了岂不可惜?
虽然这篇文章主要和技术相关,但我的生活其实也并不只有技术啦:我有微单和无人机,玩通了 Switch 上的塞尔达和马里奥,花名(雪碧)和公众号名(彩色相簿)都来自白学,毕业以来除了公费游历了北上广沪杭等大城市,也去了台湾、新加坡、捷克、法国(包括白学家的圣地斯特拉斯堡)等相当有趣的地方。只要在生活里保持开放的心态,总能认识到许多比自己更厉害的人并向他们学习。如果按照论文致谢的方式列出个名单,那么我感觉对我影响最大的是这么几位,即便其中的一些人我还未曾谋面:
<<<前端是我一辈子的信仰,写代码直到50岁>>>
关注微信公众号:web前端学习圈
回复关键词【简书】送你50G最适合2020年学习的web前端零基础入门教程(视频+笔记+素材+源码+项目实战)
另外公众号每天都会分享学习方法,知识干货,实战案例,面试技巧,经验分享等相关文章,关注web前端学习圈=关注5000+前端大牛
我司的小米和糖饼在对技术的态度和团队管理上都给我起到了很棒的「模范带头作用」,在这个团队的成长体验是最好的。
Slate 的作者 Ian Storm Taylor 在我初入开源社区时给了我很多热情而不失严格的 Review,他的框架设计理念对我有很深的影响。
携程的工业聚向我展示了代码逻辑所能达到的优雅水平。聚聚总结的 GSP 编码风格即便在编写渲染引擎时都十分适用,扭转了我对函数式编程矫枉过正的态度。
Photopea 的作者 Ivan Kutskir 在布拉格热情地招待了我,他的作品对图形学的应用给了我巨大的信心来深入这一领域。
一位不愿透露姓名的女性在我遇到困难时给了我很大的安慰,我已经习惯在每个周末去见她的动车上安心地写代码了。
在斯特拉斯堡巡礼时拍下的照片。
我还要感谢毕业到现在遇到的很多很多人,毕竟终归是和大家因缘际会的无数选择才能成就一个人。在生活这个巨大而混沌的系统里,幸运的是我们可以作出选择来结识他人、追求理想并作出改变——We are what we choose. 最后,同样感谢你的阅读噢 :D