十年來感受的前端技術變化

發表於2015-12-07

07年底,我所在的團隊需要重構一個產品,在此之前,我們的前端框架是這樣的:

  • 使用HTML Components(htc)作為基礎控制元件的實現方式,包括選項卡,樹形表格,日期選擇等控制元件。
  • 在原生js的基礎上作了一些簡單封裝,形成了包括表單校驗,彈出選單(基於popup),簡單圖表(基於XML),動態表單等功能的業務公共庫。
  • 使用XMLHTTP作為前後端通訊方式,將請求引數序列化為XML傳送給服務端,反序列化之後,反射呼叫後端服務(Java和.net),再把返回結果序列化為XML傳輸回來,用JS解析為JavaScript物件。這個傳輸方式從03年版本就開始使用,還在AJAX概念出現之前,只是一直使用的是同步傳輸方式,呼叫的時候介面會卡死。
  • 開發方式也是前後端分離的,前端只寫HTML和JS,後端提供介面(但並不是HTTP介面,而是服務端的類介面,然後通過一個統一的facade類去反射呼叫)。

這個時期的版本不用說,肯定都是隻支援IE的,我們當時需要相容的瀏覽器包括IE 5.0,5.5,6,後來7出來之後還需要支援7。

這個產品重構的目的是,對近幾年積累的業務需求進行整合,並且把服務端完全遷移到Java。對於前端來說,其實不做遷移也可以,但當時我們發現一個問題,FireFox這個東西突然崛起了,所以,我們從原先面臨的只支援IE,變成了可能要支援跨瀏覽器。

所以我們覺得需要把這個事情做一下,因為當時判斷,IE的份額可能會下降到70%左右,FireFox可能會佔有25-30%的份額,這個相容是有可能需要的,雖然以我們的場景,幾乎全部面向行業使用者,可以把瀏覽器限制得很死,但也有部分使用者自服務的產品,將來還有擴大的趨勢。

這時候我們問題就很大了,因為前端的基礎功能面臨大改,一些校驗之類的庫好辦,通訊封裝也好辦,基於htc的控制元件就是個大問題了,必須全部重寫。

當時幾個成員產生了熱烈的討論,jQuery那時候還沒有一家獨大,也沒有產生這種趨勢,可選項包含:

  • ExtJS這樣的全整合框架
  • jQuery,prototype,mootools,Ext Core這樣的核心js庫加外圍

在這兩個選擇裡,我們排除了第一個,因為雖然看上去它很符合我們的業務場景,但我們的定製化需求比較多,不確定有能力在這個基礎上做定製,可控性不好。

所以我們就決定選一個核心js庫,然後自己開發外圍控制元件。那麼,選誰呢?最終選的是prototype,因為大家判斷,我們不需要類似class的機制,這樣就把後面兩個排除了,而我們不需要太多DOM方面的封裝,因為最後業務上需要直接操作DOM的東西不會很多,都會被我們封裝掉,所以又把jQuery排除了。

所以我們的需求其實也很明確,就是有一定基礎功能的js核心庫。然後就是在這個基礎上開發控制元件了,時間也很倉促,大約只有2-3個人,2-3個月,最後幾個東西:

  • 資料表格
  • 樹形表格
  • 日期選擇(我們的日曆需求比較變態,因為有伊朗和尼泊爾客戶,所以會有波斯日曆和尼泊爾日曆之類。。。)
  • 分頁
  • 動態表單
  • 國際化方案
  • 其他一些基礎功能,佈局指引之類

最後,因為趕時間,這個版本的框架最終並未跨瀏覽器,部分控制元件的功能還是使用了IE only的特性……更致命的是,我們因為沒時間,所以在業務開發指引上花的時間很少,這導致業務開發人員趕工的時候,整個就不可控了,因為我們後面還參與了業務開發,十多個寫HTML和JS的人,被業務壓著走,壓根沒人有時間看FireFox的狀況……

後面幾年中,這個大版本被作為基礎版本,拉了無數分支出來,期間,面臨了IE8之類的相容性修改,除此之外,已經沒有機會再修改基礎庫的部分了。

09年底,我主導這個產品下一代版本的前端框架選型。我們現在回頭看這個時間點,會發現很尷尬,當時流行的,或者即將流行的所有前端方案,其實也都是第一代的理念,在現在這個時候,都已經被時代大潮沖刷得死傷殆盡了。

所以我當時非常糾結,我是預見到前端這幾年的亂象的,當時大家炒得最火的概念是什麼,是HTML5,然而我看了這方面的一些資料,發現廣義上,更多提供的是一些功能方面的東西,或者能提升佈局方式,等等,這與我們想要的東西相去甚遠,好比說,我們想要蒸汽機之類的東西,發現手裡將要有的還只是各種鐵塊。

我當時想要什麼呢?想想我當時有過什麼,經歷過什麼,早在05年初,我就有了一種設想,那就是前端的全元件化開發,當時的老大問我對現有專案有什麼看法,我提出了一個方案:

  • 擴充套件htc的使用場景,不限於基礎元件,把業務介面也全部元件化
  • 各元件可以獨立通過XMLHTTP與服務端互動,也就是說,你把一個已經除錯好的元件加到介面之後,什麼都不用管,它自己是能夠管理與服務端的通訊的,然後你只要管跟它怎麼互動就行了
  • 在此基礎上,考慮介面的動態定製,像積木一樣拼裝業務介面

這個想法在當時確實比較激進,所以當然沒人支援。但我在05-09這幾年的開發過程中,目睹各種業務程式碼的混亂,覺得可能還是應該推進元件化的開發理念。理念是有了,細節怎麼處理呢,因為HTML Components這一規範被廢棄之後,我發現現有的任何方案都不再能夠像原先那樣簡便地封裝元件了,如果對於業務開發人員來說,開發業務元件的代價過高,那工程成本更加不可控。

回想我們使用HTML Components的時候,開發一個元件易如反掌,就像開發普通頁面一樣簡單,只需在頭部宣告對外的屬性、方法、事件即可,樣式也是隔離的,JS作用域也是隔離的,使用起來也是非常簡單,就是一個自定義標籤,而且是客戶端的(區別於taglib之類的服務端標籤封裝)。

以05年時候那個產品的場景而言,絕大部分介面都是普通的配置和管理,每個介面的獨立性都較高,並不存在強整合的場景,所以是否使用元件化方式開發,並不是很重要,這跟我前幾天在微博上說:“輕量級管理控制檯有至少100種做法”是一個意思。

但是後來的做的,有包括CRM和呼叫中心之類的強整合產品,不再是原先那種單選單配置頁面了,整個產品幾乎就是一個選單,然後通過各種互動去觸發一系列功能,在這種場景下,元件化就變成了一個重要的實施手段。

所以我萬般無奈,就把目光投到04年開始持續關注的一項技術:Adobe Flex,在這個東西剛出來的時候,我曾經預言微軟會推出一個精簡的CLR,作為瀏覽器外掛執行,與Flash平臺抗衡,後來大家看到了SilverLight,不過這個東西當時的普及度並不好,第一代極其簡陋,第二代稍微好一點,所以我沒有考慮它。

在2009年,Adobe Flex算是比較成熟了,帶有完善的元件化機制,生命週期管理,強大而易於擴充套件的基礎控制元件,優雅而強大的開發語言,但我當時有判斷,這東西是一個走下坡路的平臺,而且Adobe自身有很大不足,也沒有能力把它支撐和運營好。最終,我反覆權衡,仍然選擇了使用它來構建這個版本。

所以當時我面臨的壓力非常大,公司內部的辯論達到白熱化,正反雙方都有相當多的人蔘與,而且反對者還略多些,年輕開發者居多。在有資格參與這項決策的各基層管理者和技術專家組的投票來看,雙方也是基本對等的。這個爭論現在回頭看,很有意思,雙方考慮的事情其實不在一個層面上,從基層開發者的角度,他會從流行趨勢方面進行一個判斷,覺得Flash必死,HTML永生,你讓我1948年加入國民黨,是何居心?

但從我們有些老員工的角度,看到的問題是由於公司開發人員水平參差不齊,導致很多程式碼寫得非常糟糕,很難除錯,也很難重構,更沒有一些全域性檢視,讓我們能看到程式碼的結構是怎樣,資料的流動又是怎樣,產品的質量如何,我們覺得對於大部分低層次開發者來說,JavaScript的約束還是太弱了,這樣鬆散的語言在他們手裡會造成開發效率極低,bug率很高。

當時我有個比喻,我們相當於在2000年選擇了使用Delphi。這個比喻是為了向公司高層說明,我們到底是在幹什麼,大家在這麼激烈地吵什麼。因為他們雖然不一定熟悉當時的技術方案,但都是從2000年那個時代過來的,對Delphi的盛衰還是很熟悉的。

最終我們的判斷是:5年之內,泛HTML體系的元件化方案不會有一個相對穩定的選擇,我們是做企業軟體產品的,並不害怕使用的技術相對過時,怕的是時常有劇烈變動,而且不能掌控。所以,打算用Adobe Flex這樣的東西,來做一個5-8年的支撐,在這段時期內,見機行事,在泛HTML體系裡作一些探索,跟進可能會流行的技術,並且加以積累。另外,選擇一種輕量級的解決方案,用於構建面向個人使用者的門戶,這條線選擇的是jQuery和BootStrap。

當時在輕量級這條線的解決方案上,我也有一些意見,因為在我們當時的開發團隊中,大家對“控制元件”這個東西的依賴程度太高了,絕大部分人壓根不具備我們現在所說的前端工程師的基本能力,只有呼叫控制元件的能力。而我認為,輕量級的場景中,應當嚴格控制“控制元件”的使用,絕大部分場景都是用基本樣式加一些DOM操作就可以做到,當時我們的輕量場景是面向運營商的使用者們,他們登入上去,查詢話費,辦理業務,兌換積分,購買一些服務或者禮品之類。

所以,當我後來發現這樣的場景也引入了上M的js庫的時候,我的心情是非常崩潰的,後來有的團隊考慮在這種業務場景下封裝一些服務端標籤,這個事情我並不贊同。很多時候,我們需要去使用服務端模板之類的方式生成介面,為的是頁面效能優化之類,但我們所在的場景,並不需要這麼苛刻的優化,頁面DOM結構是比較簡單的,反倒是JS這塊需要嚴格控制。這麼輕量級的場景,做服務端元件化的必要性也不是很大,只需引入js的模板庫就可以了。

回頭再看Flex這頭的狀況,我們用這個東西針對重量級的管理系統進行開發,但令人痛苦的是,絕大部分開發人員很難理解“元件化”這麼一件事情,比如說,仍然保留“頁面”這個稱呼,越是老開發人員越是難改變思維,我有一次很激動地說:我們這種模式下,哪裡來的頁面?我們做的是一個軟體系統,你可以把它理解為執行在瀏覽器中的桌面軟體,只存在元件,不存在頁面,頁面是HTML體系中特有的稱呼。

所以,做一個全業務元件化的實踐,需要一次又一次從理念上去向業務團隊灌輸,什麼是元件,元件樹是怎樣的,元件跟資料層如何通訊,元件之間如何通訊等等,如何提取合適的元件,不把這些理念灌輸下去,整個產品也很難成功。

舉例來說,之前系統規劃人員增加了新模組的時候,一般是把資料庫表結構截圖發出來,然後最多畫個草圖表示介面,但在元件化的開發方式中,這中間至少還有一步元件規劃、整合的過程,這一步應當需要嚴謹的考慮。

我所在的基礎技術團隊中,也有不少人對我們要面臨的問題看得不太清楚,把責任想得太輕。之前那種簡單的管理頁面,只需要基礎技術團隊提供基礎元件和公共庫,公共樣式的開發,但在全元件化的實踐中,做完這些事情也才完成了整個事情的30%。

除了這些,還需要對業務團隊培訓元件化的相關理念,需要跟蹤他們的開發過程,評審、觀察、糾偏,還需要構建元件的管控、測試平臺,否則,仍然是一種山寨的開發流程,而享受不到元件化所應當擁有的流水化生產體驗。這一步其實沒有做下去,投入還是太少了。

這個大產品版本以及附屬專案一共有好幾十名開發人員參與,歷時2年多,雖然離我心目中的目標還有不少差距,但在某些方面的提升是能夠感受得出來的,比如說開發效率的提升。除此之外,這個版本的美觀程度比之前所有版本都好,終於有現代氣息了,誰說企業使用者就都是土鱉的,甚至有一次還收到過外籍市場人員專門的郵件誇讚,這讓我們感到很振奮。

開發效率的提升另一方面還源自Flex自身的特點,獨立於瀏覽器的外掛,可以說,大部分前端開發人員都會面臨跨瀏覽器的折騰,尤其是那幾年,亂得可怕,相容問題會把人折磨死,我們面對的客戶,從歐美到亞非拉,各種低端高階瀏覽器並存,所以我們是繞開了這個問題,而這麼複雜的元件介面,如果使用傳統Web技術,在IE6、7等瀏覽器下的效能會慘不忍睹,基於外掛體系的另外一個優點是,效能不受低端瀏覽器的制約,下限比較高。

在這個階段,我們實際上是做了比較取巧的選擇,但從長期角度看,還是必須回到泛HTML體系的。所以,從12年開始,我就開始考慮未來的產品技術選型。

在12年這個點上,我們能看到的東西還是比較多的,至少比幾年前有了很明顯的發展,比如說,jQuery終於一家獨大了,比如說,AMD和CMD之類js模組封裝機制的出現,比如說,Backbone,Knockout,Angular分別出現了不少使用者,比如說,ES6和Web Components規範的落地快看到曙光了。

但實事求是地說,這些所有東西加起來,開發體驗還是比不過Adobe Flex,在重量級場景下,泛HTML體系的整合力度還是比較弱,對開發人員的技能要求也會高一些。我也關注Dart,關注TypeScript,(為什麼不關注CoffeeScript,因為所在公司幾乎全是隻懂Java的開發人員,跟Coffee的理念差別太大,推廣成本極高)。

感慨歸感慨,選型還是一直要做,只是這個選型在實際有專案應用前,一直還會處於動態調整中。

在這段時間中,我們考察了Backbone,Knockout和Angular,也持續關注了司徒正美的Avalon,最終還是比較傾向於Angular的。

傾向於Angular的主要原因是,開發團隊經過磨合,已經逐步習慣了元件化的開發方式和資料繫結所帶來的開發理念的改變,Angular在這方面與Flex比較接近,學習成本很低,而且那些稍微繁瑣的配置,對於這些習慣了Java的人員來說,並非負擔。

所以,當時我們的基礎技術團隊也花了不少時間來學習Angular,看原始碼,踩坑,期間,團隊的大漠窮秋翻譯出版了國內第一本Angular中文書籍。

我們研究Angular,還有另外一個原因。在基於Flex的這代產品逐步穩定之後,又實現了一個二次開發平臺,從資料模型的動態定義(類似ORM),到規則、流程、服務的動態定義,再到介面的視覺化配置,動態的資料繫結。這個平臺的有些方面考慮是不夠成熟的,尤其是動態介面這塊,細節不展開了。

所謂的動態資料繫結,指什麼呢,其實核心機制類似於Angular的這種繫結關係。在Flex體系中,資料繫結是通過Proxy機制實現的,對POJO有一定程度的封裝,比如Object就封裝為ObjectProxy,而Array封裝為ArrayCollection。我們當時需要建立的是:基於單個使用者(或者會話)的資料聯動體系。

這個是什麼意思呢,對於某個登入使用者來說,他所可能擁有的全部資料結構是可預測的,絕大部分是平級關係,沒有關聯,部分存在聯動關係,我們要做的就是把這些關係描述出來,用視覺化的形式配置,掛接在ORM機制上作為外殼,並且提供給業務流程、業務規則和動態介面作為繫結資料來源。

這個資料的定義和描述機制,用現有技術類比,就好像是Meteor或者Relay,但當時我們對有些東西沒有考慮清楚。因為對於某個使用者而言,他的所有資料結構與介面上的元件結構是無關的,這意味著,如果不把資料做拆解傳遞,一旦遇到元件樹上的不同層級指向資料來源同一位置之類的情況,就不好辦。因為我們元件之間也是不共享資料的,類似現在的React。所以我們當時的問題就是資料中間層設計得不好。

以我們當時的設計,其實是適合Angular這種機制的,Angular裡面,不同層級的元件之間,資料可以有穿透,比如說你在最頂層繫結了一個dataStore,在隔了很多級的葉子節點上仍然可以繫結到dataStore.a.b這樣的資料,這個a.b資料路徑可以與葉子節點在元件樹上的路徑毫無關係。

在這個過程中,我還是一直在嘗試考慮基礎框架與元件化業務開發的結合,包括整個元件和資料的規劃流程,管控機制等,也寫了幾篇文章,現在回頭看,有一些不成熟的地方,另外有些東西當時覺得有坑,但正好被近期出現的新技術填平了。

舉例來說,當時我覺得,不管是AMD還是CMD,都是在JS模組自身程式碼中宣告依賴項,如果是出現程式碼路徑調整之類的情況,這些依賴項的變更就是個問題,所以我設想了一種類似npm的方式,用資料庫集中存放依賴關係,模組自身的內容不維護這個關係,然後構建階段生成出來。

今年看到Webpack,裡面提到module,bundle,entry chunk之間的關係,感到已經差不多把我當時考慮的那些東西全部做到了,社群的力量真是強大啊。

在之前爭論Flex方案的時候,反對方的有些觀點很典型,比如:Flex程式碼編寫之後還需要編譯,JS的不用。但其實最近幾年我們看到,Web應用的開發過程逐漸離不開構建環節,不可能再像十年前那樣,寫完程式碼之後,最多隻做下壓縮就上線了,那是一種很原始的生產方式,所以,從這個方面看,元件化、構建過程,這些都是現代富Web應用開發方案中必不可少的部分,已經逐漸被廣泛接受了。最近兩三年,業界對前端工程化的關注度也已經比以前高很多了,令人欣慰。

前面這些年,我們還有一個大的缺陷,那就是一直對非同步程式設計模型關注太少了,在早期我們一直使用同步的XMLHTTP,後來到了用Flex的時候,改用非同步的HTTP通訊,使用AMF協議傳輸資料,我們的經驗仍然停留在使用回撥函式這種初級的方案上,間或使用事件,並不知道任何跟promise之類理念相關的東西,所以程式碼有不少很不優雅,這個跟眼界有關,還是缺少學習所致。

現在距離09年底已經整整6年過去了,回頭看來,6年前可能接觸到的任何前端領域的框架或者庫,到今天都已經全部是過時的。這6年時間,我們可能積累出一些經驗,但大部分東西都不可避免地失效了,這就是野蠻生長期的痛。

這6年裡,我們看到AMD,CMD這些模組機制逐步流行,然後又突然衰落,Angular為代表的前端MV*框架橫空出世,帶給前端社群巨大的衝擊,React挾元件化和Virtual DOM之力快速崛起,你方唱罷我登臺,亂花漸欲迷人眼。

兩年前我從之前公司離職,心中充滿迷茫,未來的Web技術會怎樣發展,重型Web應用應當採用怎樣的整體方案去建造,自己並不能說出一個精確答案。今天我們再要考慮未來的Web應用技術選型,已經沒有兩年前那麼迷茫了,這兩年仍然在持續學習,持續思考,每次面臨選型,還是如履薄冰,一再審視專案型別,人員狀況。

未來的前端Web應用技術選型還有不小的變數,身為大齡前端技術人員,一方面感慨有些自己熟知的技術逐步落幕消亡,另外一方面又看到新事物不斷出現,以種種方式改進和衝擊著我們的開發方式。生在這個時代是一種不幸,也是幸運。毛主席教導我們:三天不學習,不如劉少奇。說到底,保持學習和思考,是現在這個階段最該做的。舊技術雖然消亡了,但它們留下的思維啟發永在。

後記:最近一直有種衝動,想把過去十年(2005-2015)時間所經歷的一些事情總結一下,昨晚開始寫,今天中午完善了一下,大致寫完,意猶未盡,與大家共勉。

相關文章