撰寫本文的時候筆者閱讀了以下文章,不可避免的會借鑒或者引用其中的一些觀點與文字,若有冒犯,請隨時告知。文列如下:
Thoughts about React, Redux & javascript in 2016
順便推廣下筆者總結的泛前端知識點綱要總結:Coder Essential之客戶端知識索引(iOS/Android/Web)、Coder Essential之編程語言學習知識點綱要、Coder Essential之編程語言語法特性概論
幾年前初入大學,才識編程的時候,崇尚的是一路向下,那個時候喜歡搞Windows、Linux底層相關的東西,覺得那些做網頁的太Low了。直到后來偶然的機會接觸到HTML、JavaScript、CSS,很長一段時間覺得這種這么不嚴謹的,毫無工程美學的搭配不過是詩余罷了。后來,深入了,才發現,能夠有幸在這片星辰大海里游蕩,可以以幾乎領先于其他方向的技術變革速度來感受這個時代的脈動,是多么幸運的一件事。這是一個最壞的時代,因為一不小心就發現自己Out了;這也是一個最好的時代,我們永遠在前行。繁華漸欲,萬馬齊鳴!
借用蘇寧前端結構師的總結,任何一個編程生態都會經歷三個階段,第一個是原始時期,由于需要在語言與基礎的API上進行擴充,這個階段會催生大量的Tools。第二個階段,隨著做的東西的復雜化,需要更多的組織,會引入大量的設計模式啊,架構模式的概念,這個階段會催生大量的Frameworks。第三個階段,隨著需求的進一步復雜與團隊的擴充,就進入了工程化的階段,各類分層MVC,MVP,MVVM之類,可視化開發,自動化測試,團隊協同系統。這個階段會出現大量的小而美的Library。當然,筆者以Tools-Frameworks-Library只是想說明我個人感覺的變化。
筆者從jQuery時代一路走來,經歷了以BootStrap為代表的基于jQuery的插件式框架與CSS框架的興起,到后面以Angular 1為代表的MVVM框架,以及到現在以React為代表的組件式框架的興起。從最初的認為前端就是切頁面,加上一些交互特效,到后面形成一個完整的webapp,總體的變革上,筆者以為有以下幾點:
移動優先與響應式開發
前端組件化與工程化的變革
從直接操作Dom節點轉向以狀態/數據流為中心
筆者在本文中的敘事方式是按照自己的認知過程,夾雜了大量個人主觀的感受,看看就好,不一定要當真,畢竟我菜。梳理來說,有以下幾條線:
交互角度的從PC端為中心到Mobile First
架構角度的從以DOM為中心到MVVM/MVP到以數據/狀態為驅動。
工程角度的從隨意化到模塊化到組件化。
工具角度的從人工到Grunt/Gulp到Webpack/Browserify。
在正文之前,重要的事情說三遍,我是菜鳥!我是菜鳥!我是菜鳥!從來都沒有最好的技術,而只有合適的技術與懂它的人。我感謝這些偉大的類庫/框架,感恩它們的Contributor,給我呈現了一個何其廣闊的世界。雖然2015的前端領域有點野蠻生長,但是也體現了前端一直是開源領域的扛鼎之處,希望有一天我也能為它的繁榮做出自己的貢獻。
基石與催化劑
瀏覽器的躍進
現在H5已經成為了一個符號,基本上所有具有絢麗界面或者交互的Web界面,無論是PC還是Mobile端,都被稱為基于H5。筆者一直認為,H5技術的發展以及帶來的一系列前端的變革,都離不開現代瀏覽器的發展與以IE為典型代表的老的瀏覽器的消逝。目前瀏覽器的市場分布可以由如下兩個圖:
瀏覽器分布圖
國際瀏覽器分布圖
這里順嘴說下,如果想要明確某個屬性是否可以使用可以參考Can I Use。話說雖然微信內置的某X5內核瀏覽器連Flexbox都不支持,不過它幫我們屏蔽了大量手機的底層差異,筆者還是非常感恩的。當然了,在有了Webpack之后,用Flexbox不是問題,可以查看這嘎達。
ECMAScript
2015年是JavaScript誕生的20周年。同時又是ES6標準落地的一年。ES6是迄今為止 ECMAScript標準最大的變革(如果不算上胎死腹中的ES4的話),帶來了一系列令開發者興奮的新特性。從目前es的進化速度來看,es后面應該會變成一個個的feature發布而不是像以前那樣大版本號的方式,所以現在官方也在推薦 ES+年份這種叫法而不是 ES+版本。在ES2015中,筆者覺得比較欣賞的特性如下,其他完整的特性介紹可以參考這篇文章ES6 Overview in 350 Bullet Points。
Module & Module Loader:ES2015中加入的原生模塊機制支持可謂是意義最重大的feature了,且不說目前市面上五花八門的module/loader庫,各種不同實現機制互不兼容也就罷了(其實這也是非常大的問題),關鍵是那些模塊定義/裝載語法都丑到爆炸,但是這也是無奈之舉,在沒有語言級別的支持下,js只能做到這一步,正所謂巧婦難為無米之炊。ES2016中的Module機制借鑒自 CommonJS,同時又提供了更優雅的關鍵字及語法(雖然也存在一些問題)。
Class:準確來說class關鍵字只是一個js里構造函數的語法糖而已,跟直接function寫法無本質區別。只不過有了Class的原生支持后,js的面向對象機制有了更多的可能性,比如衍生的extends關鍵字(雖然也只是語法糖)。
Promise & Reflect API:Promise的誕生其實已經有幾十年了,它被納入ES規范最大意義在于,它將市面上各種異步實現庫的最佳實踐都標準化了。至于Reflect API,它讓js歷史上第一次具備了元編程能力,這一特性足以讓開發者們腦洞大開。
除此之外,ES2016的相關草案也已經確定了一大部分其他new features。這里提兩個我比較感興趣的new feature:
async/await:協程。ES2016中 async/await 實際是對Generator&Promise的上層封裝,幾乎同步的寫法寫異步比Promise更優雅更簡單,非常值得期待。
decorator:裝飾器,其實等同于Java里面的注解。注解機制對于大型應用的開發的作用想必不用我過多贅述了。用過的同學都說好。
更讓人興奮的是,JavaScript慢慢不再局限于前端開發中,NodeJs的提出讓人們感受到了利用JavaScript進行全棧開發的能力,從此大大提高了開發的效率(至少不用多學習一門語言)。JavaScript在物聯網中的應用也曾經引起一些追捧與風潮,不過今年物聯網社區更加冷靜地看待著這個問題,但是并不影響各大廠商對于JavaScript的支持,可以參閱javascript-beyond-the-web-in-2015這篇文章。筆者還是很看好JavaScript在其他領域繼續大放異彩,畢竟ECMAScript 6,7已經是如此的優秀。
WebAssembly
WebAssembly 選擇了跟ES2015在同一天發布,其項目領頭人是大名鼎鼎的js之父Brendan Eich。WebAssembly旨在解決js作為解釋性語言的先天性能缺陷,試圖通過在瀏覽器底層加入編譯機制從而提高js性能。WebAssembly所做的正是為Web打造一套專用的字節碼,這項標準在未來應用場景可能是這樣的:
開發應用,但使用任何一門可被編譯為WebAssembly的語言編寫源代碼。
用編譯器將源代碼轉換為WebAssembly字節碼,也可按需轉換為匯編代碼。
在瀏覽器中加載字節碼并運行。
需要注意的是,WebAssembly不會替代JavaScript。越來越多的語言和平臺想在Web上大展手腳,這會迫使JavaScript和瀏覽器廠商不得不加快步伐來補充缺失的功能,其中某些功能通過復雜的JavaScript語義來實現并不合適,所以WebAssembly可以作為JavaScript的補集加入到Web陣營中來。WebAssembly最一開始的設計初衷就是作為不依賴于JavaScript的編譯目標而存在,進而獲得了主流瀏覽器廠商的廣泛支持。很期待有一天WebAssembly能夠發展起來,到那個時候,我們用JavaScript編寫的應用也會像現在用匯編語言寫出的大型程序的感覺咯~
漸隱的jQuery與服務端渲染
HTML:附庸之徒
前端用于數據展示
在筆者最早接觸前端的時候,那個時候還不知道前端這個概念,只是知道HTML文件可以在瀏覽器中顯示。彼時連GET/POST/AJAX這些概念都不甚明了,還記得那個時候看到一本厚厚的AJAX實戰手冊不明覺厲。筆者閱讀過Roy Thomas Fielding博士的Architectural Styles andthe Design of Network-based Software Architectures這篇論文,也就是RESTful架構風格的源處。在這篇文章里,筆者反而感覺最有感觸的是從BS到CS架構的躍遷。一開始我覺得網頁是典型的BS的,咋說呢,就是網頁是數據、模板與樣式的混合,即以經典的APS.NET、PHP與JSP為例,是由服務端的模板提供一系列的標簽完成從業務邏輯代碼到頁面的流動。所以,前端只是用來展示數據。
那個時候筆者更菜,對于CSS、JS都不甚明了,一切的數據渲染都是放在服務端完成的。筆者第一次學HTML的時候,驚呆了,臥槽,這能算上一門語言嘛?太簡單了吧。。。原來做個網頁這么簡單啊,然后生活就華麗麗打了臉。那個時候,根本不會以script或者link的方式將資源載入,而是全部寫在一個文件里,好吧,那時候連jQuery都不會用。記得那個時候Ajax都是自己手寫的,長長的毫無美感的大量重復冗余的代碼真是日了狗。
為什么說HTML只是附庸之徒呢,那個時候我們沒有把Browser的地位與其他的Client并列,換言之,在經典的Spring MVC框架里,如下所示,用戶所有的邏輯操作的核心我們都會放置到Java代碼中,根本不會想到用JavaScript進行控制。另一個方面,因為沒有AJAX的概念,導致了每次都是表單提交-后臺判斷-重新渲染這種方式。這樣導致了每一個渲染出來的網頁都是無狀態的,換言之,網頁是依賴于后端邏輯反應不同有不同的呈現,自身沒有一個完整的狀態管理。
圖片來源于《前端篇: 前端演進史》
AJAX與客戶端開發
筆者最早的區分CS與BS架構,抽象來說,會認為CS是客戶端與服務器之間的雙向通信,而BS是客戶端與服務端之間的單向通信。換言之,網頁端本身也變成了有狀態。從初始打開這個網頁到最終關閉,網頁本身也有了一套自己的狀態,而擁有這種變化的狀態的基礎就是AJAX,即從單向通信變成了雙向通信。圖示如下:
漸隱的jQuery
jQuery作為了影響一代前端開發者的框架,是Tools的典型代表,它留下了璀璨的痕跡與無法磨滅的腳印。筆者在這里以jQuery作為一個符號,來代表以Dom節點的操作為核心的一代的前端開發風格。那個年代里,要插入數據或者更改數據,都是直接操作Dom節點,或者手工的構造Dom節點。譬如從服務端獲得一個用戶列表之后,會通過構造節點的方式將數據插入到Dom樹中。
但是不得不承認,在未來相當長的一段時間內,jQuery并不會直接退出歷史的舞臺,筆者個人認為一個重要的原因就是現在仍然存在著很大比重的各式各樣的基于jQuery的插件或者應用,對于崇尚拿來主義的我們,不可避免的會繼續使用著它。
jQuery引領了一個輝煌的時代,但是隨著技術的演進它也慢慢在很多項目中隱去。jQuery這個框架本身非常的優秀并且在不斷的完善中,但是它本身的定位,作為早期的跨瀏覽器的工具類屏蔽層在今天這個瀏覽器API逐步統一并且完善的今天,逐漸不是那么關鍵。因此,筆者認為jQuery會逐漸隱去的原因可能為:
現代瀏覽器的發展與逐步統一的原生API
由于瀏覽器的歷史原因,曾經的前端開發為了兼容不同瀏覽器怪癖,需要增加很多成本。jQuery 由于提供了非常易用的 API,屏蔽了瀏覽器差異,極大地提高了開發效率。這也導致很多前端只懂 jQuery。其實這幾年瀏覽器更新很快,也借鑒了很多 jQuery 的 API,如querySelector,querySelectorAll和 jQuery 選擇器同樣好用,而且性能更優。
前端由以DOM為中心到以數據/狀態為中心
jQuery 代表著傳統的以 DOM 為中心的開發模式,但現在復雜頁面開發流行的是以 React 為代表的以數據/狀態為中心的開發模式。應用復雜后,直接操作 DOM 意味著手動維護狀態,當狀態復雜后,變得不可控。React 以狀態為中心,自動幫我們渲染出 DOM,同時通過高效的 DOM Diff 算法,也能保證性能。
不支持同構渲染與跨平臺渲染
React Native中不支持jQuery。同構就是前后端運行同一份代碼,后端也可以渲染出頁面,這對 SEO 要求高的場景非常合適。由于 React 等流行框架天然支持,已經具有可行性。當我們在嘗試把現有應用改成同構時,因為代碼要運行在服務器端,但服務器端沒有 DOM,所以引用 jQuery 就會報錯。這也是要移除 jQuery 的迫切原因。同時不但要移除 jQuery,在很多場合也要避免直接操作 DOM。
性能缺陷
jQuery的性能已經不止一次被詬病了,在移動端興起的初期,就出現了Zepto這樣的輕量級框架,Angular 1也內置了jqlite這樣的小工具。前端開發一般不需要考慮性能問題,但你想在性能上追求極致的話,一定要知道 jQuery 性能很差。原生 API 選擇器相比 jQuery 豐富很多,如document.getElementsByClassName性能是$(classSelector)的 50 多倍!
說這么多,只是想在以后技術選型的時候,能有一個通盤考慮,畢竟,這是曾經的Best Love。
蛋疼的模塊化與SPA
如果當時的移動網絡速度可以更快的話,我想很多SPA框架就不存在了。
隨著踩得坑越來越多與類似于Backbone、AngularJs這樣的更加純粹全面的客戶端框架的興起,Single Page Application流行了起來。至此,在網頁開發領域也就完全變成了CS這種理念。至此之后,我們會考慮在前端進行更多的用戶交互與狀態管理,而不是一股腦的全部交給后臺完成。特別是頁面的切換與不同數據的呈現不再是需要用戶進行頁面的跳轉,從而在弱網情況下使用戶獲得更好的體驗與更少的流量浪費。與此同時,前端就變得更加的復雜化,我們也迫切的需要更加完善的代碼分割與管理方案,?于是,筆者開始嘗試接觸模塊化的東西。筆者自RequireJs、SeaJs興起以來一直關注,但是從未在實際項目中投入使用。額,第一次用這兩個框架的時候,發現貌似需要對現有的代碼或者喜歡的jQuery Plugins進行封裝,當時我這種懶人就有點心理陰影了。不過SeaJs作為早期國人開發的有一定影響力的前端輔助工具,筆者還是非常敬佩的。
模塊化的進步與不足
在筆者知道模塊化這個概念之前,文件夾是這么分的:
看上去非常的工整,但是稍微有個多人協作的項目,或者稍微多用一點jQuery的插件,看著那十來二十個不知道里面到底是啥的JS文件,筆者是崩潰的。筆者最早打算使用模塊化的動力來源于避免作用域污染,那個時候經常發現的問題是一不小心引進來的兩個第三方文件就打架了,你還不知道怎么去修改。
模塊一般指能夠獨立拆分且通用的代碼單元,在ES6正式出來規范之前,我們會選擇使用RequireJs或者SeaJs來進行有點像依賴注入的東西:
require([
'Tmpl!../tmpl/list.html','lib/qqapi','module/position','module/refresh','module/page','module/net'
],function(listTmpl,QQapi,Position,Refresh,Page,NET){
大概是這樣子的,但是筆者就是覺得好煩啊,并且它整個頁面的邏輯還是面向過程編碼的。換言之,我如果頁面上稍微換了個布局或者有那么三四個有交集的頁面,那就日了狗了,根本談不上復用。
Backbone.js:MVC方式的SPA
Backbone是筆者較早期接觸到的,以數據為驅動的一種框架。Backbone誕生于2010年,和響應式設計出現在同一個年代里,但他們似乎在同一個時代里火了起來。如果CSS3早點流行開來,似乎就沒有Backbone啥事了。不過移動網絡還是限制了響應式的流行,只是在今天這些都有所變化。換言之,就是將數據的處理與頁面的渲染分離了出來。算是在以jQuery那種以DOM操作為核心的基礎上完成了一次變革。同樣的筆者用過的框架還有easy-ui,不過它是一個封裝的更加完全的框架。開發時,不需要考慮怎么去寫大量的HTML/CSS代碼,只需要在他的組件內填充不同的邏輯與配置即可。很方便,也很不方便,記得筆者想稍稍修改下他的表格的功能都蛋疼了好一陣子。
Backbone相對而言會更開放一點,在筆者大量使用Angular的時候也有同學提議使用Backbone + avaon這種更輕量級的方案。我們用Ajax向后臺請求API,然后Mustache Render出來,這里已經會開始將Web端視作一個完整的Client而不僅僅是個附庸的存在。一個典型的Backbone組件的代碼如下:
//《前端篇: 前端演進史》
define([
'zepto',
'underscore',
'mustache',
'js/ProductsView',
'json!/configure.json',
'text!/templates/blog_details.html',
'js/renderBlog'
],function($,_,Mustache,ProductsView,configure,blogDetailsTemplate,GetBlog){
?
varBlogDetailsView=Backbone.View.extend({
el:$("#content"),
?
initialize:function() {
this.params='#content';
},
?
getBlog:function(slug) {
vargetblog=newGetBlog(this.params,configure['blogPostUrl'] +slug,blogDetailsTemplate);
getblog.renderBlog();
}
});
?
returnBlogDetailsView;
});
可以看見,在Backbone中已經將DOM元素與數據渲染以及邏輯剝離了開來,這樣就有助于進行團隊內的分工與協作,以及大量的代碼復用。那個時候經常會將Backbone與Angular進行對比,二者各有優劣。Backbone在顯示模板、創建數據綁定和連接組件方面給使用者更多的選擇。與之相反,Angular為這些問題提供了規定的方案,不過在創建模型與控制器方面的限制就比較少一些。筆者當時是因為想要用一套Framework來解決問題,所以還是投入了Angular的懷抱。
AngularJs 1.0:MVVM方式的SPA
AngularJs是第一個我真正喜歡的Framework,不僅僅是因為它提出的MVVM的概念,還有因為它自帶的DI以及模塊化的組織方式。或許正是因為使用了AngularJs 1.0,筆者才沒有深入使用RequireJs、SeaJs這些吧。AngularJs 1.0的優秀與槽點就不細說了,在那個時代他成功讓筆者有了一點完整的前端項目的概念,而不是多個分離的互相之間跳轉的HTML文件。最近,AngularJs 2.0終于出了Beta版本,筆者也一直保持關注。不過個人感覺唱衰的聲音還是會大于褒揚之聲,從筆者個人感覺而言,一個大而全的框架可能不如多個小而美的框架更加的靈活,關于這個對比可以參考下文的Web Components VS Reactive Components這一章節。此外,對于AngularJs 中一直詬病的性能問題,Facebook提出的Virtual DOM的算法毫無疑問為前端的性能優化指明了一條新的道路,筆者這里推薦一個Performance Benchmarks,其中詳細對比了多個DOM操作的庫。筆者在這里只貼一張圖,別的可以去原文查看:
總體而言,Vue偏輕量,適合移動端,ng適應pc端,avalon適合兼容老瀏覽器的項目。雖然Vue.js現在也有組件化的實現,包括類似于Flux的Vuex這樣的Single State Tree的框架,但是筆者還是比較傾向于把它當做一個MVVM模型來對待。
組件化的未來與Mobile-First
最初隨著React的風靡,組件化的概念深入人心。筆者一直堅信組件化是非常值得去做的事情,它在工程上會大大提升項目的可維護性及拓展性,同時會帶來一些代碼可復用的附加效果。但這里要強調的一點是,組件化的指導策略一定是分治而不是復用,分治的目的是為了使得組件之間解耦跟正交,從而提高可維護性及多人協同開發效率。如果以復用為指導原則那么組件最后一定會發展到一個配置繁雜代碼臃腫的狀態。組件化最著名的標準無疑是W3C制定的Web Components標準,它主要包含以下幾個方面:
模板能力
ShadowDom 封裝組件獨立的內部結構
自定義原生標簽
imports解決組件間的依賴
不過這個標準本身還沒發揚光大就被Angular、React這樣的框架完爆了,不過他還是指明了我們組件化的幾個準則:
資源高內聚:有點像Vue提到的理念,Single File Component。組件資源內部高內聚,組件資源由自身加載控制
作用域獨立:內部結構密封,不與全局或其他組件產生影響
自定義標簽:可以像使用HTML的預設標簽一樣方便地使用組件
可相互組合:組件正在強大的地方,組件間組裝整合
接口規范化:組件接口有統一規范,或者是生命周期的管理
Web Components VS Reactive Components
對于Web組件化的典型代表,應該是React與Angular 2。Angular 2基本上完全革了Angular 1的命,Angular開發團隊最早于2014年3月提出路線圖,直到2015年底才進入alpha階段。筆者自Angular 2開發之始就一直保持關注,見證了其規范或者接口的更迭。不可否認Angular 2在性能以及設計理念上都會比Angular 1先進很多,但是隨著2014年中到2015年初以React為代表的組件式UI框架以及Flux/Redux為代表的響應式數據流驅動興起,可能Angular 2并不會達到Angular 1的高度。筆者也在斷斷續續地更新一些Angular 2的指導與學習文檔,不過確實,除了從零開始的大型項目,Angular 2還是太笨重了。
Will Angular 2 be a success? You bet!,注意,評論更精彩
實際上,在我們選擇一個庫或者所謂的框架時,為我們的組件選擇一個合適的抽象可能會比覺得哪個框架更好更有意義。目前Web的組件化開發分為兩個大的趨勢,一個是以Angular 2、Polymer為代表的Web Components,另一個是以React、Vue、Riot為代表的Reactive Components。目前Web Components方面因為各個庫之間無法就如何定義它們達成一致,導致了類似于Angular 2、Aurelia這樣的框架用它們自己的核心來定義Web Components。只有Polymer 100%實踐了Web Components的規范。Web Components有點類似于Google,而React更像Facebook。
另外,當我們選擇一個框架時,還需要考慮清楚我們是需要一個包含了所有的功能的固執己見的框架,就像Angular2、Ember 2這樣的,還是一系列小的專精的框架的組合,就像React、Flux以及React Router這樣的。當然,我們在選擇一個框架時還必須考慮進它潛在的變化的代價與難度,以及與其他的技術集成的難度,還有就是他有沒有一個完善的生態系統。
就像筆者在自己的AARF提及的,無論前后端,在這樣一樣敏捷式開發與快速迭代地背景下,我們需要更多獨立的分離的可以方便組合的類似于插件一樣的模塊。
響應式解決方案
隨著WAP的出現與移動智能終端的飛速普及,開發者們不得不面臨一個問題,大量的流量來自于手機端而不再是PC端,傳統的PC端布局的網頁,在手機上顯示的根本不友好,什么鬼!最早的時候人們考慮的是面向PC端與WAP設計不同的頁面,不過這樣就毫無疑問將原來的工作量乘以二,并且產品管理與發布上也會存在著一定的問題,特別是在那個組件化與工程化理念還沒有流行的時代里。于是,人們開始設計一套能夠針對不同的屏幕響應式地自反饋的布局方案,也就是這里提到的響應式設計。
響應式設計不得不提到的一個缺點是:他只是將原本在模板層做的事,放到了樣式(CSS)層來完成。復雜度同力一樣不會消失,也不會憑空產生,它總是從一個物體轉移到另一個物體或一種形式轉為另一種形式。
筆者最早接觸到的響應式設計來自于BootStrap,它的Media Query功能給當時的筆者很大的驚喜的感覺。特別是CSS3中Flexbox的提出,更是能方便地踐行響應式設計的原則。不過,就以淘寶首頁為例,如果用響應式方式完成一套代碼在PC端與手機端不同的完全適應的展示效果,我覺得還不如直接寫兩套呢。不可否認響應式設計在例如菜單啊,瀑布流布局啊這些功能組件上起到了非常巧妙的作用,但是為了單純的追尋響應式布局而把整個CSS的邏輯判斷搞得那么復雜,那我是拒絕的。特別是現在組件化這么流行的今天,我寧可在根控件中自由的組織各個組件,也好過不斷地自適應判斷。
筆者不是非常提倡響應式解決方案來解決從PC端到移動端的遷移,筆者個人覺得PC端和移動端就是額,不是同一種畫風的東西。話說筆者接觸過不少完全用代碼控制的響應式布局,譬如融云的Demo,它可以根據你顯示器屏幕控制元素的顯隱和事件。不可否認設計很精巧,但是在沒有組件的那個時候,這種代碼復雜度和性價比,在下服了。筆者在自己的實踐中,對于純移動端的響應式開發,譬如微信中的H5,還是比較喜歡使用pageResponse這種方式或者它的一些改進版本。
移動優先
響應式解決方案,代表著隨著不同的分辨率下智能的響應式布局。而移動優先的概念,筆者認為則是在界面設計之初即考慮到適應移動端的布局。當然,還有一個方面就是要照顧到移動端的瀏覽器的語法支持度、它的流量以及各種各樣的Polyfill。
Hybrid:WebView VS Cross Compilation
筆者很懶,最早的時候只是有一點Android開發經驗,那個時候Hybrid技術剛剛興起,天天看DZone上N多的炫耀自己的Hybrid開發多快、性能多好的文章,立馬激發起了我的懶癌。寫一波就能跨平臺運行,多爽啊!Hybrid技術分為兩個大的分支,一個以Cordova為代表的基于系統的WebView與本地調用。另一種早期以Titanium、Tamarin,如今以React Native這樣為代表的Cross Compilation,即跨平臺編譯技術。
在我們需要學習C語言的時候,GCC就有了這樣的跨平臺編譯。
在我們開發桌面應用的時候,QT就有了這樣的跨平臺能力。
在我們構建Web應用的時候,Java就有了這樣的跨平臺能力。
在我們需要開發跨平臺應用的時候,Cordova就有了這樣的跨平臺能力。
于是乎,在筆者第一次正式創業時,我斬釘截鐵的跟投資人說,用Hybrid開發,用Cordova,沒錯的。記得那時候筆者還不懂iOS開發,所以在第一次正式做App的時候選擇了Ionic 1.0。其實最早是打算用jQuery Mobile,不過寫了第一個小的tab的Demo然后在自己的千元機上運行的時候,打開應用竟然花了20多秒,當時投資人看到的時候臉是綠的,心是涼的。估計是那時候還不會用jQuery Mobile吧(雖然現在也不會),但確實不是一個可行方案。后來筆者轉到了Ionic 1.0,確實一開始感覺不錯,速度還闊以。但是當時筆者還小,犯了一個很大的認知錯誤,就是打算完全摒棄掉Native全部用Web技術開發,于是,一個簡單地文件上傳分分鐘就教我做了人。最后產品做出來了,但是壓根用不了。插一句,一開始為了在Android老版本設備上解決WebView的兼容性問題,打算用Crosswalk。筆者第一次用Crosswalk編譯完成之后,嚇尿了。速度上確實快了一點,但是包體上實在增加的太大了,臣妾做不到啊!至此之后,筆者熄滅了完全依賴于Cordova進行APP開發的理念。
結果時間軸又錯了,人們總是超前一個時期做錯了一個在未來是正確的決定。大概是那個時候機器性能還不是足夠的好吧。
Cordova或者Webview這種方向是沒錯的,現在也大量的存在于筆者的APP中,但是對于中大型APP而言,如果直接架構在Cordova之上,筆者還是不推薦的。Build Once,Run Everywhere,貌似做不到了,或者說差強人意。那就考慮Learn Once,Write Everywhere。React Native又引領了一波時代潮流。
Cross Compilation的典型代表是NativeScript與React Native。筆者自然是更喜歡React Native的,畢竟背靠整個React生態圈,對于原生組件的支持度也是很好的。React框架本身雖好,但是還是有許多可以與之媲美的優秀的框架的,但是React依靠Virtual DOM以及組件化等概念,依賴Facebook工程師強大的工程與架構能力,已經打造了一個完整的生態。特別是0.14版本之后的react與react-dom的分割,愈發的可以看出React的雄心壯志。將表現層與具體的界面分離開來,通過Canvas、Native、Server乃至未來的Desktop這樣不同的渲染引擎,保證了代碼的高度重用性,特別是邏輯代碼的重用性。
工程化與Builder
前端工程化
大部分時候我們談論到工程化這個概念的時候,往往指的是工具化。但是任何一個通向工程化的道路上都不可避免的會走過一段工具化的道路。筆者最早的接觸Java的時候用的是Eclipse,那個時候不懂什么構建工具,不懂發布與部署,每次要用類庫都要把jar包拷貝到Libs目錄下。以至于多人協作的時候經常出現依賴相互沖突的問題。后來學會了用Maven、Gradle、Jenkins這些構建和CI工具,慢慢的才形成了一套完整的工作流程。前端工程化的道路,目標就是希望能用工程化的方法規范構建和維護有效、實用和高質量的軟件。
筆者個人感覺的工程化的要素,會有以下幾個方面:
統一的開發規范(語法/流程/工程結構)與編譯工具。實際上考慮到瀏覽器的差異性,本身我們在編寫前端代碼時,就等于在跨了N個“平臺”。在早期沒有編譯工具的時候,我們需要依賴自己去判斷瀏覽器版本(當然也可以用jQuery這樣人家封裝好的),然后根據不同的版本寫大量的重復代碼。最簡單的例子,就是CSS的屬性,需要加不同的譬如-o-、-moz-這樣的前綴。而這樣開發時的判斷無疑是浪費時間并且存在了大量的冗余代碼。開發規范也是這樣一個概念,JavaScript本身作為腳本語言,語法的嚴謹性一直比較欠缺,而各個公司都有自己的規范,就像當年要實現個類都有好幾種寫法,著實蛋疼。
模塊化/組件化開發。在一個真正的工程中,我們往往需要進行協作開發,之前往往是按照頁面來劃分,但是會造成大量的重復代碼,并且維護起來會非常麻煩。這里的模塊化/組件化開發的要素與上面的第一點都是屬于開發需求。
統一的組件發布與倉庫。筆者在使用Maven前后會有很大的一個對比感,沒有一個統一的中央倉庫與版本管理工具,簡直就是一場災難。這樣也無法促進開發者之間的溝通與交流,會造成大量的重復造輪子的現象。
性能優化與項目部署。前端的錯誤追蹤與調試在早期一直是個蛋疼的問題,筆者基本上每次都要大量的交互才能重現錯誤場景。另一方面,前端會存在著大量的圖片或者其他資源,這些的發布啊命名啊也是個很蛋疼的問題。當我們在構建一個webapp的完整的流程時,我們需要一套自動化的代碼質量檢測方案來提高系統的可靠性,需要一套自動化以及高度適應的項目發布/部署方案來提高系統的伸縮性和靈活性。最后,我們需要減少冗余的接口、冗余的資源請求、提高緩存命中率,最終達到近乎極致的性能體驗。
Webpack
Webpack跟browserify本質上都是module bundler,差異點在于Webpack提供更強大的loader機制讓其更變得更加靈活。當然,Webpack的流行自然還是離不開背后的react 跟facebook。但是從現在HTTP/2標準的應用及實施進展來看,Webpack/browserify這種基于bundle的打包工具也面臨著被歷史車輪碾過的危機,相對的基于module loader的jspm反而更具前景。Browserify 可以讓你使用類似于 node 的 require() 的方式來組織瀏覽器端的 Javascript 代碼,通過預編譯讓前端?Javascript 可以直接使用 Node NPM 安裝的一些庫。相較于Webpack,Browserify具有更悠久的歷史,記得當年還是看這篇文章才開始慢慢認識到Webpack,那時候Webpack還是一個相當年輕的框架啊。
Webpack是前端開發真正意義上成為了工程級別,而不再是隨意,可以看看這篇文章。筆者第一次看Webpack的時候,沒看懂。當時用Gulp用的正順手,不需要自己往HTML文件里引入大量的Script文件,還能自動幫你給CSS加前后綴,自動地幫你壓縮,多好啊。不過Grunt和Gulp現在存在的問題就是需要自己去組裝大量的插件,參差不齊的插件質量導致了大量時間的浪費。并且Gulp/Grunt還并不能稱為一個完整的編譯工具,只是一個輔助工具。
Webpack還有很令筆者欣慰的一點,它支持Lazy Load Component,并且這種懶加載技術是與框架無關的。這樣就避免了筆者在編碼時還需要考慮固定的組件或者代碼分割,畢竟在一個快速迭代的項目中還是很難在一開始就規劃好全部的組件分割。這一點對于筆者這種被SPA JS加載以及原來的無論是基于Angular的懶加載還是React Router的懶加載折磨的人是一個大大的福音。同時,Webpack還支持配合了React Hot Loader的代碼熱插拔,可以大大地提高代碼的開發效率。畢竟等著Browserify編譯好也是很蛋疼的。
在筆者的個人的感觸中,Webpack是促成了前端真正工程化的不可缺少的一環。記得之前看過美團的前端技術分享,它提出了前端分布式編譯系統。大型系統的分布式編譯很常見,但是在前端,這典型的腳本與解釋執行的領域,出現了大型分布式編譯系統,還是很讓人震驚的。筆者是個懶惰的人,懶人總希望可以用一套方法去解決全部的問題,所以慢慢的筆者完全切入到了Webpack。或許未來某天也會離開Webpack,就像離開jQuery一樣,但是會永遠記得陪我走過的這些歲月。
響應式數據流驅動的頁面
現代這樣一個云計算與大數據的時代,Data Driven的概念早已深入人心。隨著WEB應用變得越來越復雜,再加上node前后端分離越來越流行,那么對數據流動的控制就顯得越發重要。筆者在開篇就提及過,前端變革的一個核心路線就是從以DOM Manipulation為核心到以State為核心,這樣也就能將邏輯控制、渲染與交互給分離開來。用一個函數來表示,現在的渲染就是:?。在React中?可以看做是那個render函數,可以將state渲染成Virtual DOM,Virtual DOM再被React渲染成真正的DOM。在控制器中,我們不需要關心DOM是如何變更的,只需要在我們的業務邏輯中完成狀態轉變,React會自動將這個變更顯示在UI中。其實在Angular中也是這樣,只不過Angular中采取的數據雙向綁定與臟檢測的技術,而React中采用的是JSX這樣來完成一種從狀態到頁面的綁定。
這樣一種以響應式數據流驅動的頁面,毫無疑問會將編程工作,特別是復雜的交互與邏輯處理變得更加明晰,也方面了產品迭代與變更,也就是敏捷式開發的理念。采用這樣的響應式數據流驅動的方式,還有一個很大的好處就是方便錯誤追蹤與調試。SPA State is hard to reproduce!而在Redux這樣的框架中,存在著類似于Global State Object這樣的可以將頁面全部還原,來重現Bug的東西。當測試人員/用戶遇到問題的時候,主動將當時的State發送給開發人員,開發人員就闊以直接根據State來還原現場咯。Immutable的魅力正在于此,靈活的可追蹤性。
Redux是在flux的基礎上產生的,在此基礎上它引入了函數式編程、單一數據源、不可變數據、中間件等概念,基本思想是保證數據的單向流動,同時便于控制、使用、測試。Redux不依賴于任意框架(庫),只要subscribe相應框架(庫)的內部方法,就可以使用該應用框架保證數據流動的一致性。Redux在一定程度上可以說是今年React生態甚至整個前端生態中影響最大的一個框架,它給整個前端技術棧引入了很多新成員,盡管這些概念可能在其他領域已經有了廣泛的應用。筆者還是比較推崇響應式開發的,實際工作中用的比較多的還是FPR的一些實現,譬如RxJava啊這些。Redux標榜的是Immutable的State Tree,而Vue采用的是Mutable的State Tree。
筆者在不長的代碼之路上從Windows Developer 到 Pentester,到 Android Developer,到 Server-Side Developer,最后選擇了Front-end 作為自己的歸宿。不過Server-Side Architecture 和 Data Science也是我的最愛,哈哈哈哈哈哈,怎么有一種坐擁后宮的趕腳~
希望能永遠在這條路上,心懷激情,熱淚盈眶。