我的前端之路

Gitbook Repo

撰寫本文的時候筆者閱讀了以下文章,不可避免的會借鑒或者引用其中的一些觀點(diǎn)與文字,若有冒犯,請隨時告知。文列如下:

RePractise前端篇: 前端演進(jìn)史

前端的變革

致我們終將組件化的Web

我感覺到的前端變化

解讀2015之前端篇:工業(yè)時代 野蠻發(fā)展

前端工程化知識要點(diǎn)回顧&思考

Thoughts about React, Redux & javascript in 2016

順便推廣下筆者總結(jié)的泛前端知識點(diǎn)綱要總結(jié):Coder Essential之客戶端知識索引(iOS/Android/Web)、Coder Essential之編程語言學(xué)習(xí)知識點(diǎn)綱要、Coder Essential之編程語言語法特性概論

幾年前初入大學(xué),才識編程的時候,崇尚的是一路向下,那個時候喜歡搞Windows、Linux底層相關(guān)的東西,覺得那些做網(wǎng)頁的太Low了。直到后來偶然的機(jī)會接觸到HTML、JavaScript、CSS,很長一段時間覺得這種這么不嚴(yán)謹(jǐn)?shù)?,毫無工程美學(xué)的搭配不過是詩余罷了。后來,深入了,才發(fā)現(xiàn),能夠有幸在這片星辰大海里游蕩,可以以幾乎領(lǐng)先于其他方向的技術(shù)變革速度來感受這個時代的脈動,是多么幸運(yùn)的一件事。這是一個最壞的時代,因為一不小心就發(fā)現(xiàn)自己Out了;這也是一個最好的時代,我們永遠(yuǎn)在前行。繁華漸欲,萬馬齊鳴!

借用蘇寧前端結(jié)構(gòu)師的總結(jié),任何一個編程生態(tài)都會經(jīng)歷三個階段,第一個是原始時期,由于需要在語言與基礎(chǔ)的API上進(jìn)行擴(kuò)充,這個階段會催生大量的Tools。第二個階段,隨著做的東西的復(fù)雜化,需要更多的組織,會引入大量的設(shè)計模式啊,架構(gòu)模式的概念,這個階段會催生大量的Frameworks。第三個階段,隨著需求的進(jìn)一步復(fù)雜與團(tuán)隊的擴(kuò)充,就進(jìn)入了工程化的階段,各類分層MVC,MVP,MVVM之類,可視化開發(fā),自動化測試,團(tuán)隊協(xié)同系統(tǒng)。這個階段會出現(xiàn)大量的小而美的Library。當(dāng)然,筆者以Tools-Frameworks-Library只是想說明我個人感覺的變化。

筆者從jQuery時代一路走來,經(jīng)歷了以BootStrap為代表的基于jQuery的插件式框架與CSS框架的興起,到后面以Angular 1為代表的MVVM框架,以及到現(xiàn)在以React為代表的組件式框架的興起。從最初的認(rèn)為前端就是切頁面,加上一些交互特效,到后面形成一個完整的webapp,總體的變革上,筆者以為有以下幾點(diǎn):

移動優(yōu)先與響應(yīng)式開發(fā)

前端組件化與工程化的變革

從直接操作Dom節(jié)點(diǎn)轉(zhuǎn)向以狀態(tài)/數(shù)據(jù)流為中心

筆者在本文中的敘事方式是按照自己的認(rèn)知過程,夾雜了大量個人主觀的感受,看看就好,不一定要當(dāng)真,畢竟我菜。梳理來說,有以下幾條線:

交互角度的從PC端為中心到Mobile First

架構(gòu)角度的從以DOM為中心到MVVM/MVP到以數(shù)據(jù)/狀態(tài)為驅(qū)動。

工程角度的從隨意化到模塊化到組件化。

工具角度的從人工到Grunt/Gulp到Webpack/Browserify。

在正文之前,重要的事情說三遍,我是菜鳥!我是菜鳥!我是菜鳥!從來都沒有最好的技術(shù),而只有合適的技術(shù)與懂它的人。我感謝這些偉大的類庫/框架,感恩它們的Contributor,給我呈現(xiàn)了一個何其廣闊的世界。雖然2015的前端領(lǐng)域有點(diǎn)野蠻生長,但是也體現(xiàn)了前端一直是開源領(lǐng)域的扛鼎之處,希望有一天我也能為它的繁榮做出自己的貢獻(xiàn)。

基石與催化劑

瀏覽器的躍進(jìn)

現(xiàn)在H5已經(jīng)成為了一個符號,基本上所有具有絢麗界面或者交互的Web界面,無論是PC還是Mobile端,都被稱為基于H5。筆者一直認(rèn)為,H5技術(shù)的發(fā)展以及帶來的一系列前端的變革,都離不開現(xiàn)代瀏覽器的發(fā)展與以IE為典型代表的老的瀏覽器的消逝。目前瀏覽器的市場分布可以由如下兩個圖:

瀏覽器分布圖

國際瀏覽器分布圖

這里順嘴說下,如果想要明確某個屬性是否可以使用可以參考Can I Use。話說雖然微信內(nèi)置的某X5內(nèi)核瀏覽器連Flexbox都不支持,不過它幫我們屏蔽了大量手機(jī)的底層差異,筆者還是非常感恩的。當(dāng)然了,在有了Webpack之后,用Flexbox不是問題,可以查看這嘎達(dá)。

ECMAScript

2015年是JavaScript誕生的20周年。同時又是ES6標(biāo)準(zhǔn)落地的一年。ES6是迄今為止 ECMAScript標(biāo)準(zhǔn)最大的變革(如果不算上胎死腹中的ES4的話),帶來了一系列令開發(fā)者興奮的新特性。從目前es的進(jìn)化速度來看,es后面應(yīng)該會變成一個個的feature發(fā)布而不是像以前那樣大版本號的方式,所以現(xiàn)在官方也在推薦 ES+年份這種叫法而不是 ES+版本。在ES2015中,筆者覺得比較欣賞的特性如下,其他完整的特性介紹可以參考這篇文章ES6 Overview in 350 Bullet Points。

Module & Module Loader:ES2015中加入的原生模塊機(jī)制支持可謂是意義最重大的feature了,且不說目前市面上五花八門的module/loader庫,各種不同實(shí)現(xiàn)機(jī)制互不兼容也就罷了(其實(shí)這也是非常大的問題),關(guān)鍵是那些模塊定義/裝載語法都丑到爆炸,但是這也是無奈之舉,在沒有語言級別的支持下,js只能做到這一步,正所謂巧婦難為無米之炊。ES2016中的Module機(jī)制借鑒自 CommonJS,同時又提供了更優(yōu)雅的關(guān)鍵字及語法(雖然也存在一些問題)。

Class:準(zhǔn)確來說class關(guān)鍵字只是一個js里構(gòu)造函數(shù)的語法糖而已,跟直接function寫法無本質(zhì)區(qū)別。只不過有了Class的原生支持后,js的面向?qū)ο髾C(jī)制有了更多的可能性,比如衍生的extends關(guān)鍵字(雖然也只是語法糖)。

Promise & Reflect API:Promise的誕生其實(shí)已經(jīng)有幾十年了,它被納入ES規(guī)范最大意義在于,它將市面上各種異步實(shí)現(xiàn)庫的最佳實(shí)踐都標(biāo)準(zhǔn)化了。至于Reflect API,它讓js歷史上第一次具備了元編程能力,這一特性足以讓開發(fā)者們腦洞大開。

除此之外,ES2016的相關(guān)草案也已經(jīng)確定了一大部分其他new features。這里提兩個我比較感興趣的new feature:

async/await:協(xié)程。ES2016中 async/await 實(shí)際是對Generator&Promise的上層封裝,幾乎同步的寫法寫異步比Promise更優(yōu)雅更簡單,非常值得期待。

decorator:裝飾器,其實(shí)等同于Java里面的注解。注解機(jī)制對于大型應(yīng)用的開發(fā)的作用想必不用我過多贅述了。用過的同學(xué)都說好。

更讓人興奮的是,JavaScript慢慢不再局限于前端開發(fā)中,NodeJs的提出讓人們感受到了利用JavaScript進(jìn)行全棧開發(fā)的能力,從此大大提高了開發(fā)的效率(至少不用多學(xué)習(xí)一門語言)。JavaScript在物聯(lián)網(wǎng)中的應(yīng)用也曾經(jīng)引起一些追捧與風(fēng)潮,不過今年物聯(lián)網(wǎng)社區(qū)更加冷靜地看待著這個問題,但是并不影響各大廠商對于JavaScript的支持,可以參閱javascript-beyond-the-web-in-2015這篇文章。筆者還是很看好JavaScript在其他領(lǐng)域繼續(xù)大放異彩,畢竟ECMAScript 6,7已經(jīng)是如此的優(yōu)秀。

WebAssembly

WebAssembly 選擇了跟ES2015在同一天發(fā)布,其項目領(lǐng)頭人是大名鼎鼎的js之父Brendan Eich。WebAssembly旨在解決js作為解釋性語言的先天性能缺陷,試圖通過在瀏覽器底層加入編譯機(jī)制從而提高js性能。WebAssembly所做的正是為Web打造一套專用的字節(jié)碼,這項標(biāo)準(zhǔn)在未來應(yīng)用場景可能是這樣的:

開發(fā)應(yīng)用,但使用任何一門可被編譯為WebAssembly的語言編寫源代碼。

用編譯器將源代碼轉(zhuǎn)換為WebAssembly字節(jié)碼,也可按需轉(zhuǎn)換為匯編代碼。

在瀏覽器中加載字節(jié)碼并運(yùn)行。

需要注意的是,WebAssembly不會替代JavaScript。越來越多的語言和平臺想在Web上大展手腳,這會迫使JavaScript和瀏覽器廠商不得不加快步伐來補(bǔ)充缺失的功能,其中某些功能通過復(fù)雜的JavaScript語義來實(shí)現(xiàn)并不合適,所以WebAssembly可以作為JavaScript的補(bǔ)集加入到Web陣營中來。WebAssembly最一開始的設(shè)計初衷就是作為不依賴于JavaScript的編譯目標(biāo)而存在,進(jìn)而獲得了主流瀏覽器廠商的廣泛支持。很期待有一天WebAssembly能夠發(fā)展起來,到那個時候,我們用JavaScript編寫的應(yīng)用也會像現(xiàn)在用匯編語言寫出的大型程序的感覺咯~

漸隱的jQuery與服務(wù)端渲染

HTML:附庸之徒

前端用于數(shù)據(jù)展示

在筆者最早接觸前端的時候,那個時候還不知道前端這個概念,只是知道HTML文件可以在瀏覽器中顯示。彼時連GET/POST/AJAX這些概念都不甚明了,還記得那個時候看到一本厚厚的AJAX實(shí)戰(zhàn)手冊不明覺厲。筆者閱讀過Roy Thomas Fielding博士的Architectural Styles andthe Design of Network-based Software Architectures這篇論文,也就是RESTful架構(gòu)風(fēng)格的源處。在這篇文章里,筆者反而感覺最有感觸的是從BS到CS架構(gòu)的躍遷。一開始我覺得網(wǎng)頁是典型的BS的,咋說呢,就是網(wǎng)頁是數(shù)據(jù)、模板與樣式的混合,即以經(jīng)典的APS.NET、PHP與JSP為例,是由服務(wù)端的模板提供一系列的標(biāo)簽完成從業(yè)務(wù)邏輯代碼到頁面的流動。所以,前端只是用來展示數(shù)據(jù)。

那個時候筆者更菜,對于CSS、JS都不甚明了,一切的數(shù)據(jù)渲染都是放在服務(wù)端完成的。筆者第一次學(xué)HTML的時候,驚呆了,臥槽,這能算上一門語言嘛?太簡單了吧。。。原來做個網(wǎng)頁這么簡單啊,然后生活就華麗麗打了臉。那個時候,根本不會以script或者link的方式將資源載入,而是全部寫在一個文件里,好吧,那時候連jQuery都不會用。記得那個時候Ajax都是自己手寫的,長長的毫無美感的大量重復(fù)冗余的代碼真是日了狗。

為什么說HTML只是附庸之徒呢,那個時候我們沒有把Browser的地位與其他的Client并列,換言之,在經(jīng)典的Spring MVC框架里,如下所示,用戶所有的邏輯操作的核心我們都會放置到Java代碼中,根本不會想到用JavaScript進(jìn)行控制。另一個方面,因為沒有AJAX的概念,導(dǎo)致了每次都是表單提交-后臺判斷-重新渲染這種方式。這樣導(dǎo)致了每一個渲染出來的網(wǎng)頁都是無狀態(tài)的,換言之,網(wǎng)頁是依賴于后端邏輯反應(yīng)不同有不同的呈現(xiàn),自身沒有一個完整的狀態(tài)管理。

圖片來源于《前端篇: 前端演進(jìn)史》

AJAX與客戶端開發(fā)

筆者最早的區(qū)分CS與BS架構(gòu),抽象來說,會認(rèn)為CS是客戶端與服務(wù)器之間的雙向通信,而BS是客戶端與服務(wù)端之間的單向通信。換言之,網(wǎng)頁端本身也變成了有狀態(tài)。從初始打開這個網(wǎng)頁到最終關(guān)閉,網(wǎng)頁本身也有了一套自己的狀態(tài),而擁有這種變化的狀態(tài)的基礎(chǔ)就是AJAX,即從單向通信變成了雙向通信。圖示如下:

漸隱的jQuery

jQuery作為了影響一代前端開發(fā)者的框架,是Tools的典型代表,它留下了璀璨的痕跡與無法磨滅的腳印。筆者在這里以jQuery作為一個符號,來代表以Dom節(jié)點(diǎn)的操作為核心的一代的前端開發(fā)風(fēng)格。那個年代里,要插入數(shù)據(jù)或者更改數(shù)據(jù),都是直接操作Dom節(jié)點(diǎn),或者手工的構(gòu)造Dom節(jié)點(diǎn)。譬如從服務(wù)端獲得一個用戶列表之后,會通過構(gòu)造節(jié)點(diǎn)的方式將數(shù)據(jù)插入到Dom樹中。

但是不得不承認(rèn),在未來相當(dāng)長的一段時間內(nèi),jQuery并不會直接退出歷史的舞臺,筆者個人認(rèn)為一個重要的原因就是現(xiàn)在仍然存在著很大比重的各式各樣的基于jQuery的插件或者應(yīng)用,對于崇尚拿來主義的我們,不可避免的會繼續(xù)使用著它。

You-Dont-Need-jQuery

jQuery引領(lǐng)了一個輝煌的時代,但是隨著技術(shù)的演進(jìn)它也慢慢在很多項目中隱去。jQuery這個框架本身非常的優(yōu)秀并且在不斷的完善中,但是它本身的定位,作為早期的跨瀏覽器的工具類屏蔽層在今天這個瀏覽器API逐步統(tǒng)一并且完善的今天,逐漸不是那么關(guān)鍵。因此,筆者認(rèn)為jQuery會逐漸隱去的原因可能為:

現(xiàn)代瀏覽器的發(fā)展與逐步統(tǒng)一的原生API

由于瀏覽器的歷史原因,曾經(jīng)的前端開發(fā)為了兼容不同瀏覽器怪癖,需要增加很多成本。jQuery 由于提供了非常易用的 API,屏蔽了瀏覽器差異,極大地提高了開發(fā)效率。這也導(dǎo)致很多前端只懂 jQuery。其實(shí)這幾年瀏覽器更新很快,也借鑒了很多 jQuery 的 API,如querySelector,querySelectorAll和 jQuery 選擇器同樣好用,而且性能更優(yōu)。

前端由以DOM為中心到以數(shù)據(jù)/狀態(tài)為中心

jQuery 代表著傳統(tǒng)的以 DOM 為中心的開發(fā)模式,但現(xiàn)在復(fù)雜頁面開發(fā)流行的是以 React 為代表的以數(shù)據(jù)/狀態(tài)為中心的開發(fā)模式。應(yīng)用復(fù)雜后,直接操作 DOM 意味著手動維護(hù)狀態(tài),當(dāng)狀態(tài)復(fù)雜后,變得不可控。React 以狀態(tài)為中心,自動幫我們渲染出 DOM,同時通過高效的 DOM Diff 算法,也能保證性能。

不支持同構(gòu)渲染與跨平臺渲染

React Native中不支持jQuery。同構(gòu)就是前后端運(yùn)行同一份代碼,后端也可以渲染出頁面,這對 SEO 要求高的場景非常合適。由于 React 等流行框架天然支持,已經(jīng)具有可行性。當(dāng)我們在嘗試把現(xiàn)有應(yīng)用改成同構(gòu)時,因為代碼要運(yùn)行在服務(wù)器端,但服務(wù)器端沒有 DOM,所以引用 jQuery 就會報錯。這也是要移除 jQuery 的迫切原因。同時不但要移除 jQuery,在很多場合也要避免直接操作 DOM。

性能缺陷

jQuery的性能已經(jīng)不止一次被詬病了,在移動端興起的初期,就出現(xiàn)了Zepto這樣的輕量級框架,Angular 1也內(nèi)置了jqlite這樣的小工具。前端開發(fā)一般不需要考慮性能問題,但你想在性能上追求極致的話,一定要知道 jQuery 性能很差。原生 API 選擇器相比 jQuery 豐富很多,如document.getElementsByClassName性能是$(classSelector)的 50 多倍!

說這么多,只是想在以后技術(shù)選型的時候,能有一個通盤考慮,畢竟,這是曾經(jīng)的Best Love。

蛋疼的模塊化與SPA

如果當(dāng)時的移動網(wǎng)絡(luò)速度可以更快的話,我想很多SPA框架就不存在了。

隨著踩得坑越來越多與類似于Backbone、AngularJs這樣的更加純粹全面的客戶端框架的興起,Single Page Application流行了起來。至此,在網(wǎng)頁開發(fā)領(lǐng)域也就完全變成了CS這種理念。至此之后,我們會考慮在前端進(jìn)行更多的用戶交互與狀態(tài)管理,而不是一股腦的全部交給后臺完成。特別是頁面的切換與不同數(shù)據(jù)的呈現(xiàn)不再是需要用戶進(jìn)行頁面的跳轉(zhuǎn),從而在弱網(wǎng)情況下使用戶獲得更好的體驗與更少的流量浪費(fèi)。與此同時,前端就變得更加的復(fù)雜化,我們也迫切的需要更加完善的代碼分割與管理方案,?于是,筆者開始嘗試接觸模塊化的東西。筆者自RequireJs、SeaJs興起以來一直關(guān)注,但是從未在實(shí)際項目中投入使用。額,第一次用這兩個框架的時候,發(fā)現(xiàn)貌似需要對現(xiàn)有的代碼或者喜歡的jQuery Plugins進(jìn)行封裝,當(dāng)時我這種懶人就有點(diǎn)心理陰影了。不過SeaJs作為早期國人開發(fā)的有一定影響力的前端輔助工具,筆者還是非常敬佩的。

前端掃盲-之打造一個自動化的前端項目

模塊化的進(jìn)步與不足

在筆者知道模塊化這個概念之前,文件夾是這么分的:

看上去非常的工整,但是稍微有個多人協(xié)作的項目,或者稍微多用一點(diǎn)jQuery的插件,看著那十來二十個不知道里面到底是啥的JS文件,筆者是崩潰的。筆者最早打算使用模塊化的動力來源于避免作用域污染,那個時候經(jīng)常發(fā)現(xiàn)的問題是一不小心引進(jìn)來的兩個第三方文件就打架了,你還不知道怎么去修改。

模塊一般指能夠獨(dú)立拆分且通用的代碼單元,在ES6正式出來規(guī)范之前,我們會選擇使用RequireJs或者SeaJs來進(jìn)行有點(diǎn)像依賴注入的東西:

require([

'Tmpl!../tmpl/list.html','lib/qqapi','module/position','module/refresh','module/page','module/net'

],function(listTmpl,QQapi,Position,Refresh,Page,NET){

大概是這樣子的,但是筆者就是覺得好煩啊,并且它整個頁面的邏輯還是面向過程編碼的。換言之,我如果頁面上稍微換了個布局或者有那么三四個有交集的頁面,那就日了狗了,根本談不上復(fù)用。

Backbone.js:MVC方式的SPA

Backbone是筆者較早期接觸到的,以數(shù)據(jù)為驅(qū)動的一種框架。Backbone誕生于2010年,和響應(yīng)式設(shè)計出現(xiàn)在同一個年代里,但他們似乎在同一個時代里火了起來。如果CSS3早點(diǎn)流行開來,似乎就沒有Backbone啥事了。不過移動網(wǎng)絡(luò)還是限制了響應(yīng)式的流行,只是在今天這些都有所變化。換言之,就是將數(shù)據(jù)的處理與頁面的渲染分離了出來。算是在以jQuery那種以DOM操作為核心的基礎(chǔ)上完成了一次變革。同樣的筆者用過的框架還有easy-ui,不過它是一個封裝的更加完全的框架。開發(fā)時,不需要考慮怎么去寫大量的HTML/CSS代碼,只需要在他的組件內(nèi)填充不同的邏輯與配置即可。很方便,也很不方便,記得筆者想稍稍修改下他的表格的功能都蛋疼了好一陣子。

Backbone相對而言會更開放一點(diǎn),在筆者大量使用Angular的時候也有同學(xué)提議使用Backbone + avaon這種更輕量級的方案。我們用Ajax向后臺請求API,然后Mustache Render出來,這里已經(jīng)會開始將Web端視作一個完整的Client而不僅僅是個附庸的存在。一個典型的Backbone組件的代碼如下:

//《前端篇: 前端演進(jìn)史》

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中已經(jīng)將DOM元素與數(shù)據(jù)渲染以及邏輯剝離了開來,這樣就有助于進(jìn)行團(tuán)隊內(nèi)的分工與協(xié)作,以及大量的代碼復(fù)用。那個時候經(jīng)常會將Backbone與Angular進(jìn)行對比,二者各有優(yōu)劣。Backbone在顯示模板、創(chuàng)建數(shù)據(jù)綁定和連接組件方面給使用者更多的選擇。與之相反,Angular為這些問題提供了規(guī)定的方案,不過在創(chuàng)建模型與控制器方面的限制就比較少一些。筆者當(dāng)時是因為想要用一套Framework來解決問題,所以還是投入了Angular的懷抱。

AngularJs 1.0:MVVM方式的SPA

AngularJs是第一個我真正喜歡的Framework,不僅僅是因為它提出的MVVM的概念,還有因為它自帶的DI以及模塊化的組織方式?;蛟S正是因為使用了AngularJs 1.0,筆者才沒有深入使用RequireJs、SeaJs這些吧。AngularJs 1.0的優(yōu)秀與槽點(diǎn)就不細(xì)說了,在那個時代他成功讓筆者有了一點(diǎn)完整的前端項目的概念,而不是多個分離的互相之間跳轉(zhuǎn)的HTML文件。最近,AngularJs 2.0終于出了Beta版本,筆者也一直保持關(guān)注。不過個人感覺唱衰的聲音還是會大于褒揚(yáng)之聲,從筆者個人感覺而言,一個大而全的框架可能不如多個小而美的框架更加的靈活,關(guān)于這個對比可以參考下文的Web Components VS Reactive Components這一章節(jié)。此外,對于AngularJs 中一直詬病的性能問題,F(xiàn)acebook提出的Virtual DOM的算法毫無疑問為前端的性能優(yōu)化指明了一條新的道路,筆者這里推薦一個Performance Benchmarks,其中詳細(xì)對比了多個DOM操作的庫。筆者在這里只貼一張圖,別的可以去原文查看:

總體而言,Vue偏輕量,適合移動端,ng適應(yīng)pc端,avalon適合兼容老瀏覽器的項目。雖然Vue.js現(xiàn)在也有組件化的實(shí)現(xiàn),包括類似于Flux的Vuex這樣的Single State Tree的框架,但是筆者還是比較傾向于把它當(dāng)做一個MVVM模型來對待。

組件化的未來與Mobile-First

最初隨著React的風(fēng)靡,組件化的概念深入人心。筆者一直堅信組件化是非常值得去做的事情,它在工程上會大大提升項目的可維護(hù)性及拓展性,同時會帶來一些代碼可復(fù)用的附加效果。但這里要強(qiáng)調(diào)的一點(diǎn)是,組件化的指導(dǎo)策略一定是分治而不是復(fù)用,分治的目的是為了使得組件之間解耦跟正交,從而提高可維護(hù)性及多人協(xié)同開發(fā)效率。如果以復(fù)用為指導(dǎo)原則那么組件最后一定會發(fā)展到一個配置繁雜代碼臃腫的狀態(tài)。組件化最著名的標(biāo)準(zhǔn)無疑是W3C制定的Web Components標(biāo)準(zhǔn),它主要包含以下幾個方面:

模板能力

ShadowDom 封裝組件獨(dú)立的內(nèi)部結(jié)構(gòu)

自定義原生標(biāo)簽

imports解決組件間的依賴

不過這個標(biāo)準(zhǔn)本身還沒發(fā)揚(yáng)光大就被Angular、React這樣的框架完爆了,不過他還是指明了我們組件化的幾個準(zhǔn)則:

資源高內(nèi)聚:有點(diǎn)像Vue提到的理念,Single File Component。組件資源內(nèi)部高內(nèi)聚,組件資源由自身加載控制

作用域獨(dú)立:內(nèi)部結(jié)構(gòu)密封,不與全局或其他組件產(chǎn)生影響

自定義標(biāo)簽:可以像使用HTML的預(yù)設(shè)標(biāo)簽一樣方便地使用組件

可相互組合:組件正在強(qiáng)大的地方,組件間組裝整合

接口規(guī)范化:組件接口有統(tǒng)一規(guī)范,或者是生命周期的管理

Web Components VS Reactive Components

對于Web組件化的典型代表,應(yīng)該是React與Angular 2。Angular 2基本上完全革了Angular 1的命,Angular開發(fā)團(tuán)隊最早于2014年3月提出路線圖,直到2015年底才進(jìn)入alpha階段。筆者自Angular 2開發(fā)之始就一直保持關(guān)注,見證了其規(guī)范或者接口的更迭。不可否認(rèn)Angular 2在性能以及設(shè)計理念上都會比Angular 1先進(jìn)很多,但是隨著2014年中到2015年初以React為代表的組件式UI框架以及Flux/Redux為代表的響應(yīng)式數(shù)據(jù)流驅(qū)動興起,可能Angular 2并不會達(dá)到Angular 1的高度。筆者也在斷斷續(xù)續(xù)地更新一些Angular 2的指導(dǎo)與學(xué)習(xí)文檔,不過確實(shí),除了從零開始的大型項目,Angular 2還是太笨重了。

Will Angular 2 be a success? You bet!,注意,評論更精彩

實(shí)際上,在我們選擇一個庫或者所謂的框架時,為我們的組件選擇一個合適的抽象可能會比覺得哪個框架更好更有意義。目前Web的組件化開發(fā)分為兩個大的趨勢,一個是以Angular 2、Polymer為代表的Web Components,另一個是以React、Vue、Riot為代表的Reactive Components。目前Web Components方面因為各個庫之間無法就如何定義它們達(dá)成一致,導(dǎo)致了類似于Angular 2、Aurelia這樣的框架用它們自己的核心來定義Web Components。只有Polymer 100%實(shí)踐了Web Components的規(guī)范。Web Components有點(diǎn)類似于Google,而React更像Facebook。

另外,當(dāng)我們選擇一個框架時,還需要考慮清楚我們是需要一個包含了所有的功能的固執(zhí)己見的框架,就像Angular2、Ember 2這樣的,還是一系列小的專精的框架的組合,就像React、Flux以及React Router這樣的。當(dāng)然,我們在選擇一個框架時還必須考慮進(jìn)它潛在的變化的代價與難度,以及與其他的技術(shù)集成的難度,還有就是他有沒有一個完善的生態(tài)系統(tǒng)。

就像筆者在自己的AARF提及的,無論前后端,在這樣一樣敏捷式開發(fā)與快速迭代地背景下,我們需要更多獨(dú)立的分離的可以方便組合的類似于插件一樣的模塊。

響應(yīng)式解決方案

隨著WAP的出現(xiàn)與移動智能終端的飛速普及,開發(fā)者們不得不面臨一個問題,大量的流量來自于手機(jī)端而不再是PC端,傳統(tǒng)的PC端布局的網(wǎng)頁,在手機(jī)上顯示的根本不友好,什么鬼!最早的時候人們考慮的是面向PC端與WAP設(shè)計不同的頁面,不過這樣就毫無疑問將原來的工作量乘以二,并且產(chǎn)品管理與發(fā)布上也會存在著一定的問題,特別是在那個組件化與工程化理念還沒有流行的時代里。于是,人們開始設(shè)計一套能夠針對不同的屏幕響應(yīng)式地自反饋的布局方案,也就是這里提到的響應(yīng)式設(shè)計。

響應(yīng)式設(shè)計不得不提到的一個缺點(diǎn)是:他只是將原本在模板層做的事,放到了樣式(CSS)層來完成。復(fù)雜度同力一樣不會消失,也不會憑空產(chǎn)生,它總是從一個物體轉(zhuǎn)移到另一個物體或一種形式轉(zhuǎn)為另一種形式。

筆者最早接觸到的響應(yīng)式設(shè)計來自于BootStrap,它的Media Query功能給當(dāng)時的筆者很大的驚喜的感覺。特別是CSS3中Flexbox的提出,更是能方便地踐行響應(yīng)式設(shè)計的原則。不過,就以淘寶首頁為例,如果用響應(yīng)式方式完成一套代碼在PC端與手機(jī)端不同的完全適應(yīng)的展示效果,我覺得還不如直接寫兩套呢。不可否認(rèn)響應(yīng)式設(shè)計在例如菜單啊,瀑布流布局啊這些功能組件上起到了非常巧妙的作用,但是為了單純的追尋響應(yīng)式布局而把整個CSS的邏輯判斷搞得那么復(fù)雜,那我是拒絕的。特別是現(xiàn)在組件化這么流行的今天,我寧可在根控件中自由的組織各個組件,也好過不斷地自適應(yīng)判斷。

筆者不是非常提倡響應(yīng)式解決方案來解決從PC端到移動端的遷移,筆者個人覺得PC端和移動端就是額,不是同一種畫風(fēng)的東西。話說筆者接觸過不少完全用代碼控制的響應(yīng)式布局,譬如融云的Demo,它可以根據(jù)你顯示器屏幕控制元素的顯隱和事件。不可否認(rèn)設(shè)計很精巧,但是在沒有組件的那個時候,這種代碼復(fù)雜度和性價比,在下服了。筆者在自己的實(shí)踐中,對于純移動端的響應(yīng)式開發(fā),譬如微信中的H5,還是比較喜歡使用pageResponse這種方式或者它的一些改進(jìn)版本。

移動優(yōu)先

響應(yīng)式解決方案,代表著隨著不同的分辨率下智能的響應(yīng)式布局。而移動優(yōu)先的概念,筆者認(rèn)為則是在界面設(shè)計之初即考慮到適應(yīng)移動端的布局。當(dāng)然,還有一個方面就是要照顧到移動端的瀏覽器的語法支持度、它的流量以及各種各樣的Polyfill。

Hybrid:WebView VS Cross Compilation

筆者很懶,最早的時候只是有一點(diǎn)Android開發(fā)經(jīng)驗,那個時候Hybrid技術(shù)剛剛興起,天天看DZone上N多的炫耀自己的Hybrid開發(fā)多快、性能多好的文章,立馬激發(fā)起了我的懶癌。寫一波就能跨平臺運(yùn)行,多爽??!Hybrid技術(shù)分為兩個大的分支,一個以Cordova為代表的基于系統(tǒng)的WebView與本地調(diào)用。另一種早期以Titanium、Tamarin,如今以React Native這樣為代表的Cross Compilation,即跨平臺編譯技術(shù)。

在我們需要學(xué)習(xí)C語言的時候,GCC就有了這樣的跨平臺編譯。

在我們開發(fā)桌面應(yīng)用的時候,QT就有了這樣的跨平臺能力。

在我們構(gòu)建Web應(yīng)用的時候,Java就有了這樣的跨平臺能力。

在我們需要開發(fā)跨平臺應(yīng)用的時候,Cordova就有了這樣的跨平臺能力。

于是乎,在筆者第一次正式創(chuàng)業(yè)時,我斬釘截鐵的跟投資人說,用Hybrid開發(fā),用Cordova,沒錯的。記得那時候筆者還不懂iOS開發(fā),所以在第一次正式做App的時候選擇了Ionic 1.0。其實(shí)最早是打算用jQuery Mobile,不過寫了第一個小的tab的Demo然后在自己的千元機(jī)上運(yùn)行的時候,打開應(yīng)用竟然花了20多秒,當(dāng)時投資人看到的時候臉是綠的,心是涼的。估計是那時候還不會用jQuery Mobile吧(雖然現(xiàn)在也不會),但確實(shí)不是一個可行方案。后來筆者轉(zhuǎn)到了Ionic 1.0,確實(shí)一開始感覺不錯,速度還闊以。但是當(dāng)時筆者還小,犯了一個很大的認(rèn)知錯誤,就是打算完全摒棄掉Native全部用Web技術(shù)開發(fā),于是,一個簡單地文件上傳分分鐘就教我做了人。最后產(chǎn)品做出來了,但是壓根用不了。插一句,一開始為了在Android老版本設(shè)備上解決WebView的兼容性問題,打算用Crosswalk。筆者第一次用Crosswalk編譯完成之后,嚇尿了。速度上確實(shí)快了一點(diǎn),但是包體上實(shí)在增加的太大了,臣妾做不到??!至此之后,筆者熄滅了完全依賴于Cordova進(jìn)行APP開發(fā)的理念。

結(jié)果時間軸又錯了,人們總是超前一個時期做錯了一個在未來是正確的決定。大概是那個時候機(jī)器性能還不是足夠的好吧。

Cordova或者Webview這種方向是沒錯的,現(xiàn)在也大量的存在于筆者的APP中,但是對于中大型APP而言,如果直接架構(gòu)在Cordova之上,筆者還是不推薦的。Build Once,Run Everywhere,貌似做不到了,或者說差強(qiáng)人意。那就考慮Learn Once,Write Everywhere。React Native又引領(lǐng)了一波時代潮流。

Cross Compilation的典型代表是NativeScript與React Native。筆者自然是更喜歡React Native的,畢竟背靠整個React生態(tài)圈,對于原生組件的支持度也是很好的。React框架本身雖好,但是還是有許多可以與之媲美的優(yōu)秀的框架的,但是React依靠Virtual DOM以及組件化等概念,依賴Facebook工程師強(qiáng)大的工程與架構(gòu)能力,已經(jīng)打造了一個完整的生態(tài)。特別是0.14版本之后的react與react-dom的分割,愈發(fā)的可以看出React的雄心壯志。將表現(xiàn)層與具體的界面分離開來,通過Canvas、Native、Server乃至未來的Desktop這樣不同的渲染引擎,保證了代碼的高度重用性,特別是邏輯代碼的重用性。

工程化與Builder

前端工程化

大部分時候我們談?wù)摰焦こ袒@個概念的時候,往往指的是工具化。但是任何一個通向工程化的道路上都不可避免的會走過一段工具化的道路。筆者最早的接觸Java的時候用的是Eclipse,那個時候不懂什么構(gòu)建工具,不懂發(fā)布與部署,每次要用類庫都要把jar包拷貝到Libs目錄下。以至于多人協(xié)作的時候經(jīng)常出現(xiàn)依賴相互沖突的問題。后來學(xué)會了用Maven、Gradle、Jenkins這些構(gòu)建和CI工具,慢慢的才形成了一套完整的工作流程。前端工程化的道路,目標(biāo)就是希望能用工程化的方法規(guī)范構(gòu)建和維護(hù)有效、實(shí)用和高質(zhì)量的軟件。

筆者個人感覺的工程化的要素,會有以下幾個方面:

統(tǒng)一的開發(fā)規(guī)范(語法/流程/工程結(jié)構(gòu))與編譯工具。實(shí)際上考慮到瀏覽器的差異性,本身我們在編寫前端代碼時,就等于在跨了N個“平臺”。在早期沒有編譯工具的時候,我們需要依賴自己去判斷瀏覽器版本(當(dāng)然也可以用jQuery這樣人家封裝好的),然后根據(jù)不同的版本寫大量的重復(fù)代碼。最簡單的例子,就是CSS的屬性,需要加不同的譬如-o-、-moz-這樣的前綴。而這樣開發(fā)時的判斷無疑是浪費(fèi)時間并且存在了大量的冗余代碼。開發(fā)規(guī)范也是這樣一個概念,JavaScript本身作為腳本語言,語法的嚴(yán)謹(jǐn)性一直比較欠缺,而各個公司都有自己的規(guī)范,就像當(dāng)年要實(shí)現(xiàn)個類都有好幾種寫法,著實(shí)蛋疼。

模塊化/組件化開發(fā)。在一個真正的工程中,我們往往需要進(jìn)行協(xié)作開發(fā),之前往往是按照頁面來劃分,但是會造成大量的重復(fù)代碼,并且維護(hù)起來會非常麻煩。這里的模塊化/組件化開發(fā)的要素與上面的第一點(diǎn)都是屬于開發(fā)需求。

統(tǒng)一的組件發(fā)布與倉庫。筆者在使用Maven前后會有很大的一個對比感,沒有一個統(tǒng)一的中央倉庫與版本管理工具,簡直就是一場災(zāi)難。這樣也無法促進(jìn)開發(fā)者之間的溝通與交流,會造成大量的重復(fù)造輪子的現(xiàn)象。

性能優(yōu)化與項目部署。前端的錯誤追蹤與調(diào)試在早期一直是個蛋疼的問題,筆者基本上每次都要大量的交互才能重現(xiàn)錯誤場景。另一方面,前端會存在著大量的圖片或者其他資源,這些的發(fā)布啊命名啊也是個很蛋疼的問題。當(dāng)我們在構(gòu)建一個webapp的完整的流程時,我們需要一套自動化的代碼質(zhì)量檢測方案來提高系統(tǒng)的可靠性,需要一套自動化以及高度適應(yīng)的項目發(fā)布/部署方案來提高系統(tǒng)的伸縮性和靈活性。最后,我們需要減少冗余的接口、冗余的資源請求、提高緩存命中率,最終達(dá)到近乎極致的性能體驗。

Webpack

Webpack跟browserify本質(zhì)上都是module bundler,差異點(diǎn)在于Webpack提供更強(qiáng)大的loader機(jī)制讓其更變得更加靈活。當(dāng)然,Webpack的流行自然還是離不開背后的react 跟facebook。但是從現(xiàn)在HTTP/2標(biāo)準(zhǔn)的應(yīng)用及實(shí)施進(jìn)展來看,Webpack/browserify這種基于bundle的打包工具也面臨著被歷史車輪碾過的危機(jī),相對的基于module loader的jspm反而更具前景。Browserify 可以讓你使用類似于 node 的 require() 的方式來組織瀏覽器端的 Javascript 代碼,通過預(yù)編譯讓前端?Javascript 可以直接使用 Node NPM 安裝的一些庫。相較于Webpack,Browserify具有更悠久的歷史,記得當(dāng)年還是看這篇文章才開始慢慢認(rèn)識到Webpack,那時候Webpack還是一個相當(dāng)年輕的框架啊。

Webpack是前端開發(fā)真正意義上成為了工程級別,而不再是隨意,可以看看這篇文章。筆者第一次看Webpack的時候,沒看懂。當(dāng)時用Gulp用的正順手,不需要自己往HTML文件里引入大量的Script文件,還能自動幫你給CSS加前后綴,自動地幫你壓縮,多好啊。不過Grunt和Gulp現(xiàn)在存在的問題就是需要自己去組裝大量的插件,參差不齊的插件質(zhì)量導(dǎo)致了大量時間的浪費(fèi)。并且Gulp/Grunt還并不能稱為一個完整的編譯工具,只是一個輔助工具。

Webpack還有很令筆者欣慰的一點(diǎn),它支持Lazy Load Component,并且這種懶加載技術(shù)是與框架無關(guān)的。這樣就避免了筆者在編碼時還需要考慮固定的組件或者代碼分割,畢竟在一個快速迭代的項目中還是很難在一開始就規(guī)劃好全部的組件分割。這一點(diǎn)對于筆者這種被SPA JS加載以及原來的無論是基于Angular的懶加載還是React Router的懶加載折磨的人是一個大大的福音。同時,Webpack還支持配合了React Hot Loader的代碼熱插拔,可以大大地提高代碼的開發(fā)效率。畢竟等著Browserify編譯好也是很蛋疼的。

在筆者的個人的感觸中,Webpack是促成了前端真正工程化的不可缺少的一環(huán)。記得之前看過美團(tuán)的前端技術(shù)分享,它提出了前端分布式編譯系統(tǒng)。大型系統(tǒng)的分布式編譯很常見,但是在前端,這典型的腳本與解釋執(zhí)行的領(lǐng)域,出現(xiàn)了大型分布式編譯系統(tǒng),還是很讓人震驚的。筆者是個懶惰的人,懶人總希望可以用一套方法去解決全部的問題,所以慢慢的筆者完全切入到了Webpack。或許未來某天也會離開Webpack,就像離開jQuery一樣,但是會永遠(yuǎn)記得陪我走過的這些歲月。

響應(yīng)式數(shù)據(jù)流驅(qū)動的頁面

現(xiàn)代這樣一個云計算與大數(shù)據(jù)的時代,Data Driven的概念早已深入人心。隨著WEB應(yīng)用變得越來越復(fù)雜,再加上node前后端分離越來越流行,那么對數(shù)據(jù)流動的控制就顯得越發(fā)重要。筆者在開篇就提及過,前端變革的一個核心路線就是從以DOM Manipulation為核心到以State為核心,這樣也就能將邏輯控制、渲染與交互給分離開來。用一個函數(shù)來表示,現(xiàn)在的渲染就是:?。在React中?可以看做是那個render函數(shù),可以將state渲染成Virtual DOM,Virtual DOM再被React渲染成真正的DOM。在控制器中,我們不需要關(guān)心DOM是如何變更的,只需要在我們的業(yè)務(wù)邏輯中完成狀態(tài)轉(zhuǎn)變,React會自動將這個變更顯示在UI中。其實(shí)在Angular中也是這樣,只不過Angular中采取的數(shù)據(jù)雙向綁定與臟檢測的技術(shù),而React中采用的是JSX這樣來完成一種從狀態(tài)到頁面的綁定。

這樣一種以響應(yīng)式數(shù)據(jù)流驅(qū)動的頁面,毫無疑問會將編程工作,特別是復(fù)雜的交互與邏輯處理變得更加明晰,也方面了產(chǎn)品迭代與變更,也就是敏捷式開發(fā)的理念。采用這樣的響應(yīng)式數(shù)據(jù)流驅(qū)動的方式,還有一個很大的好處就是方便錯誤追蹤與調(diào)試。SPA State is hard to reproduce!而在Redux這樣的框架中,存在著類似于Global State Object這樣的可以將頁面全部還原,來重現(xiàn)Bug的東西。當(dāng)測試人員/用戶遇到問題的時候,主動將當(dāng)時的State發(fā)送給開發(fā)人員,開發(fā)人員就闊以直接根據(jù)State來還原現(xiàn)場咯。Immutable的魅力正在于此,靈活的可追蹤性。

Redux是在flux的基礎(chǔ)上產(chǎn)生的,在此基礎(chǔ)上它引入了函數(shù)式編程、單一數(shù)據(jù)源、不可變數(shù)據(jù)、中間件等概念,基本思想是保證數(shù)據(jù)的單向流動,同時便于控制、使用、測試。Redux不依賴于任意框架(庫),只要subscribe相應(yīng)框架(庫)的內(nèi)部方法,就可以使用該應(yīng)用框架保證數(shù)據(jù)流動的一致性。Redux在一定程度上可以說是今年React生態(tài)甚至整個前端生態(tài)中影響最大的一個框架,它給整個前端技術(shù)棧引入了很多新成員,盡管這些概念可能在其他領(lǐng)域已經(jīng)有了廣泛的應(yīng)用。筆者還是比較推崇響應(yīng)式開發(fā)的,實(shí)際工作中用的比較多的還是FPR的一些實(shí)現(xiàn),譬如RxJava啊這些。Redux標(biāo)榜的是Immutable的State Tree,而Vue采用的是Mutable的State Tree。

筆者在不長的代碼之路上從Windows Developer 到 Pentester,到 Android Developer,到 Server-Side Developer,最后選擇了Front-end 作為自己的歸宿。不過Server-Side Architecture 和 Data Science也是我的最愛,哈哈哈哈哈哈,怎么有一種坐擁后宮的趕腳~

希望能永遠(yuǎn)在這條路上,心懷激情,熱淚盈眶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評論 25 709
  • 因個人精力有限,暫停簡書的維護(hù),歡迎大家關(guān)注我的知乎https://www.zhihu.com/people/we...
    尾尾閱讀 1,317評論 3 13
  • 前段時間,公司來了個做前端的實(shí)習(xí)生,領(lǐng)導(dǎo)讓我給她講講前端方面的技術(shù)學(xué)習(xí)階段?;叵胱约航佑|前端也就1年多點(diǎn),其實(shí)也沒...
    Kavim閱讀 411評論 0 1
  • 最近幾個人的討論組和群又鬧騰起來了。 也不是些什么重要的事,你一句我一句,像是和對方報備自己每天的行程。 這樣也如...
    手撕包菜_閱讀 777評論 0 0
  • 話不多說,先上圖曝光今晚假包換的兔女郎造型!真人更是美到無f**k說! 這么好的活動當(dāng)然要先告訴你時間和地點(diǎn): 1...
    MusicAll閱讀 1,868評論 1 0

友情鏈接更多精彩內(nèi)容