時間回到 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 上的塞爾達和馬里奧,花名(雪碧)和公眾號名(彩色相簿)都來自白學,畢業以來除了公費遊歷了北上廣滬杭等大城市,也去了臺灣、新加坡、捷克、法國(包括白學家的聖地斯特拉斯堡)等相當有趣的地方。只要在生活裡保持開放的心態,總能認識到許多比自己更厲害的人並向他們學習。如果按照論文致謝的方式列出個名單,那麼我感覺對我影響最大的是這麼幾位,即便其中的一些人我還未曾謀面:
- 我司的小米和糖餅在對技術的態度和團隊管理上都給我起到了很棒的「模範帶頭作用」,在這個團隊的成長體驗是最好的。
- Slate 的作者 Ian Storm Taylor 在我初入開源社群時給了我很多熱情而不失嚴格的 Review,他的框架設計理念對我有很深的影響。
- 攜程的工業聚向我展示了程式碼邏輯所能達到的優雅水平。聚聚總結的 GSP 編碼風格即便在編寫渲染引擎時都十分適用,扭轉了我對函數語言程式設計矯枉過正的態度。
- Photopea 的作者 Ivan Kutskir 在布拉格熱情地招待了我,他的作品對圖形學的應用給了我巨大的信心來深入這一領域。
- 一位不願透露姓名的女性在我遇到困難時給了我很大的安慰,我已經習慣在每個週末去見她的動車上安心地寫程式碼了。
在斯特拉斯堡巡禮時拍下的照片。
我還要感謝畢業到現在遇到的很多很多人,畢竟終歸是和大家因緣際會的無數選擇才能成就一個人。在生活這個巨大而混沌的系統裡,幸運的是我們可以作出選擇來結識他人、追求理想並作出改變——We are what we choose. 最後,同樣感謝你的閱讀噢 :D
本文首發於我的公眾號「彩色相簿」,不圖流量不圖廣告,純屬交個朋友,歡迎關注 :)