前端開發(fā)的瓶頸與未來之路
前端開發(fā)的瓶頸到底在哪里,前端技術(shù)是否已經(jīng)走到一個十字路口,全棧化的系統(tǒng)架構(gòu)是否能改變目前的窘境?本文將根據(jù)我自己的開發(fā)經(jīng)歷談?wù)劗?dāng)下前端開發(fā)中遇到的一些問題和想法。
引子
近兩年我一直在思考的一個問題:
如果前端不用考慮性能問題、不用考慮終端兼容性、不用考慮歷史遺留問題,甚至不用考慮具體技術(shù)實現(xiàn)…
如果我們假設(shè)自己有豐富的技術(shù)儲備,同時不用考慮上面的問題,那么前端究竟 能 做出什么樣有價值的東西?
我們把時間拉到 5 年前…
如果你「那時」還是前端開發(fā)的話。上面的問題肯定是你不得不面臨的典型問題。甚至是當(dāng)時前端開發(fā)的意義所在。
- 你會為了精確還原設(shè)計稿熬夜加班,從而練就一雙像素眼;
- 你會為了解決幾個字節(jié)的性能問題研究優(yōu)化方案,以至看懂了每一個 HTTP 請求頭;
- 你也會因為某些技術(shù)問題和同事理論,最終到達(dá)到產(chǎn)品談笑風(fēng)聲的境界;
但是隨著時間的推移,前端技術(shù)的更新迭代,以及互聯(lián)網(wǎng)的發(fā)展。你會發(fā)現(xiàn)這些曾經(jīng)的問題似乎已經(jīng)不再是問題,或者說在能預(yù)見的未來 可能 不再是問題。
頁面加載性能可能不再是問題,技術(shù)上有了 HTTP2,基建上有了 5G,硬盤也越來越快。
兼容性問題慢慢淡出大家的視角,Chrome 一家獨(dú)大,微軟也不得不向它靠攏。
很多前端開發(fā)已經(jīng)具備了后端(或者說多端)的技術(shù)能力,技術(shù)儲備也可能不是問題,當(dāng)然前提是你能招到人。
定義
到底什么是前端開發(fā),前端與后端的界限在哪里?我在三年前對它的定義是:
前端為 界面、交互展示負(fù)責(zé); 后端為 數(shù)據(jù)、業(yè)務(wù)邏輯負(fù)責(zé);
不過現(xiàn)在看來似乎已經(jīng)過時了,我越來越覺得不應(yīng)該有這樣一個清晰的界限把前后端分割開來,尤其是技術(shù)層面(除了職能層面的界限有利于協(xié)作以外)。這就好比說:如果你不能打破規(guī)則,那就必將被規(guī)則束縛。
我一直認(rèn)為程序員應(yīng)該對新的技術(shù)、工具、理念有比平常人更快的適應(yīng)能力。舉個簡單的例子,我以前寫代碼通常使用 tab 縮進(jìn),后來大家都建議使用空格,剛開始嘗試換成空格肯定是拒絕的,因為讓人改變習(xí)慣是一件很難的事情。但是當(dāng)你真正為了改變做出實踐的時候,往往就會發(fā)現(xiàn)一條新大路。同樣還有加不加分號的問題。
現(xiàn)在回過頭來再看,前端在整個系統(tǒng)層面擔(dān)任的角色至少應(yīng)該是整個視圖 View 層面的。視圖層面的技術(shù)更接近軟件系統(tǒng)的上層,更感性。感性的東西就是說一個顏色,我覺得好看,他覺得不好看,完全屬于個人情感訴求。所以前端更注重與 UI、交互 以及整個產(chǎn)品層面需要解決的問題。優(yōu)秀的前端必然要具備敏銳的產(chǎn)品洞察能力。
當(dāng)然這還只是前端最基礎(chǔ)的職責(zé)所在。同時前端做為最接近產(chǎn)品的技術(shù)角色,技術(shù)才是前端真正的硬實力。
大約在去年一年的時間,我的崗位從前端轉(zhuǎn)向了后端 Java 程序員的角色。雖然只做了一年的 Java 程序員,但是對我自身的技術(shù)提升而言是最多的一年。大家可能普遍的認(rèn)為后端轉(zhuǎn)前端比較容易,前端轉(zhuǎn)后端會有門檻,實際上根據(jù)我自己的體驗來講并非如此。
Java 這門語言是商業(yè)化、成熟度特別高的語言。無論是語言本身,還是周邊框架、工具都有一套非常成熟且層次分明的系統(tǒng)化抽象。如果你有兩、三年的編程經(jīng)驗,突然讓你上轉(zhuǎn)寫 Java 是非常容易的一件事情,尤其是寫 Java web。Spring 框架已經(jīng)為程序員屏蔽了很多復(fù)雜問題,而且已經(jīng)事實上成為了各大互聯(lián)網(wǎng)公司的主流框架選型。
我特意按我自己的學(xué)習(xí)線路繪制了一張 Java 版的程序員學(xué)習(xí)線路,僅供參考:
我們可以清楚的看出來 Java 構(gòu)建的整個體系最大的特點:它是漸進(jìn)式的,一步一步地給開發(fā)者建立正向的引導(dǎo)。
當(dāng)我處在在 應(yīng)用層 階段的時候,我需要關(guān)心的只是一些概念,方法,具備基礎(chǔ)了以后就可以借助 Spring 框架入門,入門后就可以研究源碼,你會發(fā)現(xiàn) Spring 的本質(zhì)核心類 DispatchServlet,從此 Servlet 就出現(xiàn)在了你的視野。我以前上學(xué)時理解不了 java 中 Servlet 的概念,后來參加了工作又學(xué)些了 Python,再次看到 Java 中的 Servlet 的時候瞬間就明白了它就是 Python 中的 uwsgi,就是一種接口,將編程語言和服務(wù)器網(wǎng)關(guān)鏈接起來的一種規(guī)范。
然后你就可以順利進(jìn)入下一環(huán)節(jié),服務(wù)器/通信。這里你會發(fā)現(xiàn)整個網(wǎng)絡(luò)編程的核心 Socket,同樣以前上學(xué)的時候沒理解 Socket 的概念,繼續(xù)學(xué)習(xí)后你就會明白 Socket 其實就是操作系統(tǒng)提供給編程語言的一種能力,有了它就可以建立服務(wù)器與客戶端之間的通信。在這一環(huán)節(jié)中你會學(xué)習(xí)到網(wǎng)絡(luò)層 TCP/IP 協(xié)議,明白了 TCP/UDP 的區(qū)別, while (true) { socket.listen() }
建立 Socket 監(jiān)聽會有性能問題,此時你便進(jìn)入下一個抽象層次,操作系統(tǒng)和計算機(jī)原理。
為了解決「while true」監(jiān)聽連接的性能問題,你會去學(xué)習(xí)多線程技術(shù),了解并發(fā)的概念。你可能總會聽到別人討論并發(fā)和并行的區(qū)別。繼續(xù)學(xué)習(xí)后,慢慢的你就會明白:并發(fā)多用來解決網(wǎng)絡(luò)IO(硬盤)的效率問題,而并行則是為了更好的利用多/核處理器(CPU)的問題。這時你會發(fā)現(xiàn)這個階段涉及到了很多的計算機(jī)硬件知識。內(nèi)存分配、CPU計算、IO 復(fù)用等等。
像 Spring 這種框架才能真正意義上被稱做 框架 ,因為它不僅僅解決了軟件開發(fā)的問題,更重要的是 AOP/IoC 這類概念可以完全改變編程的一些理念。使用 Spring 開發(fā) web 應(yīng)用,聯(lián)合 Java 構(gòu)建出來的生態(tài),整個開發(fā)流程就像呼吸一樣自然。
Java 構(gòu)建出來的軟件開發(fā)體系就像是把程序員放進(jìn)了一個一個的層次分明的小柜子里面,進(jìn)去了以后你根本不需要關(guān)注外界是怎么樣的,做好自己那部分工作就可以了。如果你對外界有興趣可以一點點的按圖索驥跳出你原來的小柜子。即保證精力專注的同時又建立起一套有秩序的提升曲線。這一點是別的語言體系沒有的。
實際上我在轉(zhuǎn) Java 之前對 Java 有著不小的誤解,甚至轉(zhuǎn) Java 本身也不是我自己的想法。但當(dāng)你真正轉(zhuǎn)型成 Java 程序員后。看懂了數(shù)以百萬行記的代碼倉庫、維護(hù)過每秒好幾十萬的 QPS 項目、見識過百行的 SQL 的時候,你才會對 Java 和軟件開發(fā)產(chǎn)生一種敬畏之心,才會對技術(shù)才有了更深層次的理解。
這時候再回過頭來看前端,看 JavaScript,才會發(fā)現(xiàn)它們之間的區(qū)別與特點。很多之前爭論的東西也就有了結(jié)論。
瓶頸
我相信從事前端工作稍微長一點(5年以上)的人近兩年都會有一種感覺:前端似乎沒什么東西可以玩出花樣了。這是因為很多東西都已經(jīng)成為了前端事實上的主流,以前前端沒有的基建慢慢的被完善。語言、框架、可視化、跨端、游戲、工具/自動化/工程化 這些領(lǐng)域都在發(fā)展。
語言方面 TypeScript 必然是主流,無論你愿意與否,你都將不得不使用它來寫前端。框架方面 React 已經(jīng)是事實上的主流了,沒必要再做選擇題。打包工具 Webpack 也是一家獨(dú)大,雖然被很多人詬病,但是社區(qū)生態(tài)起來了,想改變就很難。跨端應(yīng)用 Electron 也不用想了,VSCode 能做好你做不好那就不是選型的問題了。2D 游戲/繪圖方面 PixiJS 6 已經(jīng)在設(shè)計中了,3D 我個人認(rèn)為就先別玩了。
這些看似成熟的體系實際上還是有很多可以挖掘的東西。如果你不深入研究,或許會認(rèn)為過兩年這些技術(shù)就穩(wěn)定了前端就可以做到大一統(tǒng)的狀態(tài)。這個想法可能就過于天真了,我舉例解釋下它們各自的瓶頸:
前/客戶端框架的瓶頸
React(并不特指 React)雖然現(xiàn)在看起來是主流,但是它本身有很多問題是沒解決的,甚至可以說是無解的。React 的本質(zhì)只是一個 UI Library,并不是框架 Framework。框架要解決的問題是系統(tǒng)層面的不是某個抽象層面的。用 React 寫過幾個項目以后你就會認(rèn)識到用 React 去寫大型項目是非常麻煩的事情,React 本身并不解決 SPA 應(yīng)用中數(shù)據(jù)流的問題,甚至沒解決狀態(tài)管理的問題(或者說狀態(tài)管理本來就是個偽命題?)。一個很簡單的父子組件之間狀態(tài)共享的問題一直沒有成熟的解決方案,hooks 這種方案更像是拆了東墻補(bǔ)西墻。
而且現(xiàn)在 React 社區(qū)彌漫著一種崇尚函數(shù)式編程的邪氣,hooks 更像是一塊遮羞布。多數(shù)人用 hooks 的原因僅僅是不想使用 Class,因為 Class 很臃腫,function 更簡單。當(dāng)然這個邏輯是沒問題的。函數(shù)確實簡單,但是如果你把一個函數(shù)里面寫上幾百行的代碼,各種 hooks 用到飛起的時候,你才會回過頭來反思如何組織代碼。如果 Class 能以一種更好/更易于理解的方式去抽象那為什么不用呢?
后/服務(wù)端框架的瓶頸
前端框架如此,基于 Node.JS 的后端框架也好不到哪兒去,難道你真的想用 Express/Koa.js 去寫大型的后端應(yīng)用?這種量級的框架連 web 開發(fā)最簡單的三層模型( 模型、視圖、控制器)支持都不完整。當(dāng)然你可能會說小型框架本來就只關(guān)注某一方面嘛,視圖和模型層的東西可以用其它三方庫解決。是的,確實可以這樣,不過你不覺得 Node.JS 的第三方庫有點太多了嗎。正如 NestJS 在文檔中提到的一個問題一樣「很多 JavaScript 類庫都沒有高效地解決一個問題 架構(gòu) 。」React/Vue/Express/Koa 這些都是相對獨(dú)立的點,沒有一個東西能把他們連接起來形成一個面,形成一種框架級別的體系。這就是架構(gòu)的問題。
這里多說一點,結(jié)合上面 Java 構(gòu)建出來的生態(tài),對比 Node.JS 的話。我借用自己打過的比喻:如果你低頭看到的是 Node.JS,那么你抬頭未必能看見 Java。假如你從事前端開發(fā) 2,3 年遇到瓶頸,想轉(zhuǎn)學(xué) Node.JS,你會學(xué)習(xí) Exporess/Koa 這類框架,但是很快你就會發(fā)現(xiàn)一個嚴(yán)重的問題:沒辦法深入下去了。因為當(dāng)你用 Express 寫完一個頁面后就面臨著各種技術(shù)上的盲點,會讓你無所適從。
我也嘗試?yán)L制一張我對 JavaScript/Node.JS 或者說大前端體系理解的一張圖:
JavaScript 體系看似前后端通吃,客戶端、 服務(wù)端甚至桌面端皆有。但是最大的問題在于:沒有一個東西能給他們建立起關(guān)系并發(fā)展成為一種體系。
插播一條娛樂看點,前兩天寫 Ruby on rails 框架的作者 DHH 發(fā)推并配圖:
大意如下:
現(xiàn)在的年輕人在 web 開發(fā)的時候是這樣的嘛?底層邏輯、純手寫連接池 + 純手工 SQL、配置文件都放在了一起。天哪!(截圖中使用的式TJ大神寫的 Express 框架)
然后 TJ 大神也回復(fù)了:
大意如下:
只有菜鳥玩家才能寫出干凈、簡潔、高性能(黑 Ruby 性能)、見名知意的 SQL,而不是去寫一個有15層的抽象。
兩者的推特對話挺有意思,大家娛樂一下。
TypeScript 語言的瓶頸
TypeScript 也主流,但是持續(xù)關(guān)注 TS 到現(xiàn)在,我發(fā)現(xiàn) TS 也遇到了瓶頸,這個瓶頸不僅來自于 TS 的設(shè)計目標(biāo)與理念,更多的還是社區(qū)及 TC39。TS 的設(shè)計初衷是 JavaScript 的超集,由于本身要編譯成 JS,這一點本質(zhì)上限制了 TypeScript 的方向,設(shè)計者對于添加一個新特性會非常謹(jǐn)慎,一者怕與 TC39 ES proposal 沖突,二者要考編譯到不同版本 JavaScript 的兼容性問題。以至于現(xiàn)在 TS 新的語言特性只會跟進(jìn) TC 39 發(fā)布的最新 ES proposal。但是我個人對于 TC 39 的效率及未來持懷疑態(tài)度,decorator 的提案一直還處于 Stage 2 的階段,像這種其它語言都成為標(biāo)配好幾年的事情,現(xiàn)在 JavaScript 社區(qū)還在草案(stage-2)階段。
普及下 ECMA 的標(biāo)準(zhǔn)的流程:
- stage-1:前期設(shè)想
- stage-2:正式提案(裝飾器所在的階段)
- stage-3:實現(xiàn)候選
- Stage-4:完成測試
- 各個瀏覽器 JS 引擎實現(xiàn);TypeScript 實現(xiàn)
在這個問題上我認(rèn)為其實也很好解決,開個腦洞:如果微軟想借助編程語言一統(tǒng)瀏覽器和客戶端是沒有什么不可能的。并入 TC39 組織,開發(fā)真正屬于 TypeScript 的原生引擎,奉天子以令不臣的方式也未嘗不可。
近幾年 Microsoft 對于開源的投入是肉眼可見的,微軟要發(fā)力我相信很多東西都會有翻天覆地的變化。
打包工具的瓶頸
Webpack/Babel 就更不用說了,主流中的主流。但是也是問題最嚴(yán)重的一個。Webpack/Babel 的流行恰恰從反面證明了前端的基礎(chǔ)設(shè)施有多么的爛。現(xiàn)在國外網(wǎng)友老天天叫喊著 Webpack/Babel is eval 也是挺值得深思的。我們引入了一個新工具來解決問題,卻又在不經(jīng)意之間產(chǎn)生了新問題。
前端構(gòu)建工具問題的本質(zhì)還是在于 Node.JS 的包管理工具的設(shè)計。這一點在 Node.JS 的作者 Ryan Dahl 關(guān)于 Deno 演講《10 Things I Regret About Node.js》中也有過「官方」的承認(rèn)。我相信任何一個實現(xiàn)過構(gòu)建工具的人都被 Node gyp 打敗過。node-sass, fsevent 的痛不必細(xì)說。更不用說萬年被黑的 node_modules 了,你根本不知道一個簡單的 npm install 命令會導(dǎo)致安裝成千上萬個 npm 包被安裝到你的機(jī)器上。
當(dāng)然每種編程語言對應(yīng)的包管理工具都要解決依賴問題,而且這是一個普遍的問題,腳本/解釋型編程語言尤為突出,Python/Ruby/PHP 都有這些類似的問題。或許 Go/Rust 這種把源代碼編譯打包成單個可執(zhí)行文件的方式才是好的解決方式。
未來
從前人們總是抱怨 JavaScript 這門語言,黑它、諷刺它。但是我看到的是它在一點點變好。不僅是語言層面逐步完善,工具鏈生態(tài)日趨成熟,使用它的也人越來越多。大家對它的關(guān)注程度也在提高,整個 JavaScript 開發(fā)者的水平也在向更高更強(qiáng)的方向發(fā)展。生存環(huán)境只會淘汰那些老舊不再進(jìn)化的事物,能適應(yīng)變化的才會永存。
JavaScript 這門語言有兩個其它 任何 編程語言都不具備的優(yōu)點:
- 幾乎 無所不在 且不用安裝,有瀏覽器就有 JavaScript。腳本語言意味著它能被嵌入到任何宿主環(huán)境中去:Nginx、Native應(yīng)用、硬件編程、物連網(wǎng)、嵌入式 都有它的身影
- 這門語言對于技術(shù)的更新迭代有著強(qiáng)大的 適應(yīng)能力 。JavaScript 本身的更新迭代速度導(dǎo)致它進(jìn)化速度很多,語言上的新特性會很快被運(yùn)用到生產(chǎn)環(huán)境。相比 Python 而言,這簡直是做夢,Python 2 到 3 的轉(zhuǎn)換沒人能看到真正的時間表。
當(dāng)下的前端開發(fā)狀況不由得讓我我想起蘇東坡《晁錯論》中的一段話:
天下之患,最不可為者,名為治平無事,而其實有不測之憂…
最大的問題在于,有些事物,從表面上看著平淡無奇,但實際上底層暗流涌動,似乎每一時刻都有著巨變的可能性。這也是前端開發(fā)最有趣也最有潛力的地方。
作為一名新時代的前端開發(fā)者,就是要在這看似風(fēng)平浪靜的表面之下,找到一些真正的突破點,興許只是一個簡單的想法,順應(yīng)時勢然后造就出不斐的成就也說不定呢。
無論是前端還是后端、國內(nèi)還是國外,技術(shù)才是真正的核心競爭力,只有技術(shù)革新才能提高生產(chǎn)力,而對于我們程序員來講,編程則是唯一能提升硬實力的方法。只要你心中充滿了熱情,堅持下去總會走出一條自己的路子。
分享一段小經(jīng)歷
我在 2018 年有幸參加了 TypeScirpt 的推廣大會,TypeScript 的作者 Anders Hejlsberg 親自主講。一位將近 60 歲的程序員在講臺上滔滔不絕的講技術(shù)方案,TS 的設(shè)計理念。你真的很難想像這樣一位處于「知天命」階段的老頭子(實際上很年輕)講的東西。
QA 環(huán)節(jié)有個年輕小伙問到 Anders「在中國做程序員很累、很難應(yīng)該怎么堅持下去(類似這樣的描述,細(xì)節(jié)記不清楚了)」的問題。
Anders 幾乎毫不猶豫的說出了「Passion」這個單詞。我瞬間就被打動了。因為在此之前我對于「激情」這個詞的認(rèn)識還停留在成功人士的演講說辭層面,當(dāng) Anders 親口說出 Passion 一詞的時候,讓人感覺真的是一字千金。
直到現(xiàn)在 Anders 還做為 TypeScript 的核心貢獻(xiàn)者為它提交代碼,到處奔走為 TypeScript 宣傳。
我們再回到前端,那么未來的前端到底會發(fā)展成什么樣?長期而言充滿了未知數(shù),誰也沒法預(yù)測,但是短期來講我比較關(guān)注幾個東西:
- ESBuild :一個極快的 JavaScript bundler。這個工具可以說是真正的「Game changer」。同樣是一個打包任務(wù),它快到讓你沒反應(yīng)過來就完成任務(wù)了。ESBuild 使用 Go 語言編寫,實現(xiàn)了整套 并行的 ES 解析器、代碼生成器,作者是 Figma 的 CTO(是的國外的 CTO 是要寫代碼的)。最近更新很頻繁,Vue 新的構(gòu)建工具也會基于它來做 TS 部分的打包功能。
- Deno :一個安全的 JavaScript & TypeScript 運(yùn)行時。Deno 的方向充滿了可能性,未來 deno 不僅僅可以做 JS 后端,還能和 Rust 打通,給JS注入一些原生 native 的能力,然后 Webasmbly, webGL 之類的技術(shù)都變成了可能,1.0 正式版發(fā)布日期也快到了。
- Figma :一個在線版的 Sketch,雖然功能還沒有 Sketch 強(qiáng)大,但是已經(jīng)有了設(shè)計界面的基本能力。關(guān)鍵還在于它的整個實現(xiàn)都是基于 web 技術(shù),底層 C++ 實現(xiàn)圖形的渲染、繪制,前端通過 Webasmbly 與瀏覽器 Canvas 交互,做到了讓用戶在瀏覽器端體驗到了 Native 軟件能力。像 AutoLayout 這種功能在用戶體驗上就是顛覆式的,使用的時候它很自然,沒有什么存在感。但是用了就回不去了。
如果你仔細(xì)研究一番,上面的這些新鮮東西,都是起源于前端,但又不把視野局限在前端。或許這就是前端未來的發(fā)展方向吧。