本文來自極客前端訓練營的主題公開課,非原創。html
桑世龍(狼叔),阿里巴巴前端技術專家,nodejs《狼書》做者。前端
前端發展太快了,在2004年以前,大概只要會網頁三劍客(一套強大的網頁編輯工具,最初是Macromedia公司開發的,由Dreamweaver、Fireworks、Flash三個軟件組成)就很牛了,那時候前端還比較「純潔「。在進入以Ajax爲表明的異步刷新改進用戶體驗的Web 2.0時代後,開始涌現出大量的庫,好比Prototype、jQuery、MooTools、YUI、Ext JS、kissy等,它們都還只是對瀏覽器兼容性和工具類函數的封裝,但是從Backbone、Angular 1.x相繼出現後,前端就開始熱鬧起來了,出現MVC、MVVM、IOC(控制反轉,Java著名框架Spring裏的概念)、前端路由(相似於Express、Koa等框架的路由)、Virtual DOM(虛擬DOM,經過DOM Diff算法,減小對DOM操做)、JSON API(接口規範)、JavaScript Runtime(Coffee、Babel、TypeScrpt)等等,各類框架格式如雨後春筍同樣冒出來,之前可能半年甚至更長時間纔出一個框架,如今可能幾周就有新的框架誕生,前端進入了空前的爆發階段。node
如今前端開發也複雜到了必定程度,對比以前的開發方式,能夠稱爲現代Web開發。這裏我來作一個總結,前端發展經歷了4個階段:
1.HTML/CSS/JavaScript(基礎)
2.jQuery、jQuery UI、Ext JS(曾經流行)
3.Backbone(MVC)、AngularJS、Vue.js(曾經流行)
4.組件化React、Vue.js(流行趨勢)react
目前整個大前端還屬於發展期,沒有造成固定模式,因此從趨勢上看是上升期,複雜、混亂、多樣性的結果也致使如今的前端比較吃香,被你們戲稱爲「錢端」。不少同窗會問,學習前端技術是採用漸進式方式,仍是上來就直接學React/Vue.js等框架呢?實際上,我認爲這兩種方式都存在,若是有經濟壓力就向「錢「看,能夠直接學習React/Vue.js等框架,但學會了以後,必定要把其餘3個階段補充學習完;若是沒有經濟壓力,又有時間和耐心的話,按部就班的學習是最好的方式。編程沒有捷徑,不管是哪一種,都須要腳踏實地多多練習。webpack
當你代碼寫多了,就會發現:
當你們HTML寫煩了,開始引入模板引擎。
當你們CSS寫煩了,好比不支持嵌套,開始引入CSS預處理器SASS、Less、Stylus、PostCSS等。
當你們JavaScript寫煩了,開始引入更友好的語言,熟悉Ruby的使用CoffeeScript、熟悉Java/C#的使用TypeScript。git
因而,前端開發變得複雜,並進入現代Web開發時代。github
對於Node.js來講,全部前端框架都是視圖層(View)的展示技術而已,能夠很是方便地和各類框架集成,並按照業務需求予以更好的實現。另外,全部的前端框架都採用Node.js和NPM做爲輔助開發工具,使用了大量Node.js模塊,但前端框架使用的模塊大同小異,若是熟練掌握了Node.js和NPM,對於學習前端技術來講,你要學的只是純前端的部分而已,複用價值很是高。web
和Node.js相關的前端開發模塊實在是太多了,這裏簡單列舉一些:算法
由這些模塊,也就引出了咱們今天要聊得話題——大前端工程化實踐與思考。express
先來講說構建工具。預處理器是前端高級玩法。
我常常開玩笑說之前HTML/JavaScript/CSS的時代太純潔了,如今隨便寫哪樣都要編譯/轉譯。好處是能夠藉助高級特性,提升開發效率;缺點也是極其明顯的,就是人腦要有轉換思惟。這實際上是蠻痛苦的,原本HTML/JavaScript/CSS就不夠熟練,再轉一次,對於新手來講須要一個適應過程。因此這件事仍是要辯證地看,福禍相依。
提及構建工具,大概都會想到Make、Ant、Rake、Gradle等,其實Node.js世界裏有更多實現。
構建工具的源碼都不是特別複雜,因此Node.js世界裏有很是多的實現,還有人寫過Node.js版本的Make呢,玩得很嗨!
選用構建工具最主要的目的是爲了自動化。對於須要反覆重複的任務,例如壓縮(minification)、編譯、單元測試、linting等,自動化工具能夠減輕你的勞動,簡化你的工做。尤爲是工程越複雜,自動化的價值就越大。這裏的編譯包含模板、CSS預處理語言、JavaScript友好語言等編譯,在源碼編寫時用的是高級玩法。
除了編碼編譯外,還有測試、代碼風格檢查、上線前優化(合併、壓縮、混淆),能夠說,構建系統在整個軟件工程裏無處不在。
Grunt是前端領域第一個流行的DSL(領域定義語言)風格的構建工具,它的出現對於前端工程化起到了很是好的引導做用。它經過Gruntfile來描述task,同時經過插件機制來擴展各類工程能力。
當你在Gruntfile文件正確配置好了任務,任務運行器就會自動幫你或你的小組完成大部分無聊的工做。
Grunt除了配置複雜外,還有一個問題就性能,可能不少同窗遇不到性能問題。Grunt是讀寫文件的,因此在多文件、大文件處理時是有性能瓶頸的。
Grunt的設計仍是挺精巧的,但配置起來讓人抓狂,天然會有不少不爽的人,因而Gulp就慢慢崛起了。
簡單來說,Gulp是一個Node.js寫的構建工具,基於Stream的流式構建工具,它包含大量插件。Orchestrator是Gulp底層依賴的task相關的核心庫,它定義了task執行方式和依賴,並且支持最大可能的併發,Gulp的高效即來源於此。自己Stream對大文件讀寫就很是棒,再加上上面說的種種特性,使得Gulp流行成爲必然。
Gulp的應用場景很是廣,前端項目或Node項目均可以使用,哪怕是webpack也能夠和Gulp搭配使用,經過gulp-webpack模塊便可。若是想深刻學習Gulp,能夠看一下stuq-gulp(github.com/i5ting/stuq…),文中以WeUI裏Gulp用法爲例,由淺入深,從用法到原理進行了闡述。
解耦是軟件開發領域永恆的主題,而模塊化是目前最好的解耦方式,因此從無到有、從有到成熟的演化,必然要經歷很長的路。現在的發展,源於1993年HTML建立、1995年誕生JavaScript、1996年發佈CSS1,以後就進入了原始而野蠻的開發階段。從互聯網誕生到2000年泡沫破滅,Web技術纔算真正崛起。那時候特別純潔,會寫的人就很牛了,以後Flash也曾超級火爆。在以後就到了Web 2.0時代,開始出現Ajax,開始有了各類兼容瀏覽器的庫,而後開始模塊化,在2009年誕生了Node.js,完全改變了JavaScript以及前端開發領域的開發方式,從瀏覽器端的Backbone支持MVC模式,到AngluarJS支持的MVVM,到如今的React/Vue和webpack等。
這裏我嘗試從更宏觀的視角來進行歸類,大體分5個階段,分別是原始階段、包管理器、模塊規範、模塊加載器、模塊打包器。下面我來一一說明。
腳本加載還都比較原始,方式以下:使用多個script標籤加載、手動管理順序、手動管理加載哪些。
在Web開發裏通過了不少嘗試,也作過不少齷蹉的事兒,好比
動態建立Script標籤XHR Eval、XHR Injection、$.getScript()、 Script in Iframe、Script DOM Element、Script Defer。
若是說加載比較噁心,那麼腳本順序更噁心,並且JavaScript有個「特性」,一處報錯,全部後面的都會崩潰,因此開發會很苦的維護腳本加載的順序。
若是代碼存在一個問題,更新jQuery UI版本怎麼作呢?jQuery UI依賴jQuery,先下載jQuery UI代碼,而後找到依賴的jQuery版本,再替換已有文件,以後測試,若是沒問題就直接替換了。
很明顯,直接進行文件操做是很是低效率的作法,對於版本、依賴都無法作更好的處理。因而就出現了Bower、NPM等包管理器,全部模塊升級,依賴都有包管理負責,能夠說很大程度上省去了前端的重複性工做。
模塊化加載的本質是按需加載。
比較常見的規範有3個:(1)AMD、CommonJS和ES6 Modules;(2)使用標準的模塊系統來處理依賴和導出
每一個文件是一個模塊;(3)使用模塊加載器或打包器進行處理
模塊加載器須要實現兩個基本功能:
1 實現模塊定義規範,這是模塊系統的基礎
2 模塊系統的啓動與運行
常見的好比RequireJS、Sea.js和SystemJS。以AMD的模塊加載器RequireJS爲例,一般使用RequireJS的話,咱們只須要導入RequireJS便可,不須要顯式導入其餘的JS庫,由於這個工做會交給RequireJS來作。
模塊加載器提供運行環境,可以讓遵照模塊規範的代碼在上面跑,因此模塊化的好處是很明顯的。但對於真實項目來講,你還須要構建、打包等操做。
對於開發來講,只須要關注業務模塊,不須要了解模塊加載器和構建過程,很明顯這是很是理想的,也所以產生了webpack這樣的模塊打包器。
Gulp做爲通用構建工具,它已經很是完美了。但技術變革太快了,應用各類預處理器、前端組件化,致使前端無比複雜,而webpack的出現剛恰好,完美解決了前端工程化的問題。
webpack是Node編寫的著名模塊,是打包器(bundler),不僅是支持CommonJS模塊,並且還支持更潮的ES6模塊,是目前使用極其普遍的打包器,像前端組件化的框架(React、Vue)大多都是使用webpack的。
它提供了兩個極其好的機制:loaders和plugins。
loaders:webpack認爲每一個文件都是資源模塊,針對打包構建過程當中用來處理源文件的(JSX、SCSS、Less..)統稱爲loader。
plugins:插件能夠完成更多loader不能完成的功能。插件並不直接操做單個文件,它直接對整個構建過程起做用,大多數內容功能都是基於這個插件系統運行的;固然還能夠開發和使用開源的webpack插件,來知足各式各樣的需求。好比知名插件autoprefixer、html-webpack-plugin、webpack-dev-middleware、webpack-hot-middleware。
(1)從配置文件裏找到entry point
(2)解析模塊系統
(3)解決依賴
(4)解決依賴管理(讀取、解析、解決)
(5)合併全部使用的模塊
(6)合併模塊系統的運行時環境
(7)產生打包後的文件
(1)經過<script>
加載webpack打包後的文件
(2)加載模塊運行時環境
(3)加載entry point
(4)讀取依賴
(5)解決依賴
(6)執行entry point
若是對比着理解webpack打包過程和瀏覽器加載過程,能夠對前端模塊化的演講過程可以有更好的瞭解。從定義模塊規範,到模塊化系統的運行時環境(加載器),再到更高級的打包器,這個演進的過程,也讓前端開發愈來愈簡單,這纔是打包器愈來愈火爆的緣由。
事實上從結果來看也是這樣的,工程化最終目的是讓開發者專一於寫業務模塊,其餘的事情讓打包器來作。從這個角度講,webpack仍是比Gulp更成功。
可是webpack學習曲線也是比較陡峭的,以點打面、不斷深究,是很是好的學習方式。從基本用法、概念,到tree-shaking、code spit,再到如何打包、瀏覽器如何解包,到工程化,到大規模構建如何優化,考驗你的是基礎是否紮實、原理是否真懂、是否瞭解優化思路和源碼。做爲相似模塊與Gulp進行對比,到Stream(Gulp核心),到event(Stream基類),到HTTP(Stream在HTTP裏應用),到eventloop,到C++實現的libuv和v8,太可怕了,幾乎把《狼書》的全部內容都涵蓋了。
關於webpack 的封裝實踐有不少,好比知名的af-webpack、ykit、easywebpack。
af-webpack是支付寶定製的webpack,把webpack-dev-server等Node.js模塊直接打包進去,同時對配置作了更好的處理,以及插件化。
ykit是去哪兒開源的webpack,內置Connect做爲Web server,結合dev和hot中間件,對於多項目構建提效明顯,對版本文件發佈有不錯的實踐。
easywebpack也是插件化,但對解決方案如boilerplate等作了很是多的集成,好比egg的ssr是有深刻思考的,儘管我不贊同這種作法。
框架和基本探索穩定後,你們就開始想如何更好地使用、更簡單地使用。各家大廠都在前端技術棧思考如何選型和下降成本,統一技術棧。
在Create React App(CRA)項目裏使用的是react-scripts做爲啓動腳本,它和egg-scripts相似,也都是經過約定,隱藏具體實現細節,讓開發者不須要關注構建。
UMI和CRA相似,它是一套基於螞蟻金服技術棧總結的最佳實踐,是一套零配置「約定高於配製「,開箱即用,按最佳實踐進行開發的前端框架:React全家桶 + dva + jest + antd (mobile) + less + eslint。
UMI核心是chainwebpack,將webpack複雜的配置插件化,可以讓多項目之間自由切換,消除各類webpack配置問題。
UMI思考得相對全面,從技術選型、構建,到多端輸出、性能優化、發佈等方面進行了拆分,使得UMI的邊界更爲清晰,做爲前端最佳實踐沒啥毛病,目前大多數前端組也都是相似的實現方式。說白了就是現有技術棧的組合,封裝細節,讓開發者用起來更簡單。在將來,相似的封裝還會有更多,畢竟框架層面創新不那麼多,你們就會將精力慢慢轉到應用層面。
早期比較有名的是Commander,做者是TJ,實際上是Ruby遷移到Node的版本,著名的express-generator就是基於這個模塊編寫的腳手架。
它的問題在於包的依賴有點大,對於強迫症開發者來講接受不了。因而Yargs這類的模塊應運而生,強大且依賴極少。
Yeoman是著名的腳手架模塊。經過安裝模板,在yo命令上進行擴展,很明顯這是中心化思想的結果。優勢是開發難度小,對於工程化收斂是有幫助的。
模板雖好,但仍是有點麻煩。若是模板放到Github上,更新維護都很是方便。比較典型是Vue CLI(以前版本)、saojs。
核心是ownload-git-repo,而後對裏面的文件進行模板處理。這種方式的靈活度很是高,是目前比較主流的方式。
總結一下,腳手架是在一步一步的演進,但總體來講,除了在工程化收斂上作了些優化,其餘並無特別大的優化,緣由很簡單,腳手架的複雜度作不上去。相似UMI夠複雜吧,但CLI部份內容並很少,也不須要特別靈活的配置。
關於如何用Node構建腳手架,其實用Node.js寫生成器是件很是簡單的事兒。你能夠參見《零基礎十分鐘教你用Node.js寫生成器:你只須要5步》(github.com/i5ting/writ…)。
下面看一下前端的演變過程:
最初的Java、PHP時代的純服務端渲染時代。
先後端分離,即便用JavaScript運行在客戶端,經過請求獲取服務端接口數據,藉助如JQuery、Angular、React、Vue等前端框架操做或生成頁面DOM,充分利用客戶端資源,減小服務端壓力,先後端分工明確,一直到如今還是最經常使用的開發方式。
同構開發,如Meteor、Next.js、Nuxt.js等框架,都提供了不一樣的適用場景和開發方式,但目的都是爲了同一套代碼能同時應用於服務端和客戶端。
JSP/ASP都算,固然Node渲染模板也算,Node世界模板最爲豐富。
BigPipe,雖然很老了,但分塊傳輸優勢是很是明顯的,且對瀏覽器友好。Facebook和微博、去哪兒都是受益者。Node自然支持,對res.write很友好。
基於組件寫法的SSR,好比React SSR。時代變了,SSR也要跟上。vdom + hydrate玩的能夠很嗨,連BigPipe也能夠結合起來。UMI SSR和Rax SSR將來可期。
真正的同構,即CSR和SSR寫法一致,將來再也不區分概念,在Servless裏,API和渲染都是函數。
近幾年前端技術的變化可謂翻天覆地,在選擇技術棧以前應該看清本身的應用場景,沒有最好的框架,只有最適合應用場景的框架,同構開發方式也不例外,下面我介紹一下使用同構開發的優勢和須要注意的問題。
舉一個項目的例子, github.com/ykfe/egg-re… 。它的寫法是:
核心要點有:
render是React的視圖渲染方法
getInitialProps是獲取數據方法,將返回值賦值給組件狀態
CSR經過高階組件實現
SSR經過Node執行
CSR,其實須要開發者關心React和webpack構建
Beidou是SSR解決方案,開發者關心React、egg和webpack構建
UMI SSR得益於UMI內置webpack,因此開發者只須要關注React編寫便可,部署到egg上。
在將來,咱們但願全部服務都走Serverless,這樣用戶只須要關注React寫法便可。構建是本地作的,相似於UMI。egg不須要關係,由於走的是Serverless的運行時環境。
CSR和SSR寫法一致,又能夠上Serverless,它的優勢有:
C端應用,直接SSR,能夠提效,性能有保障,SEO友好。
中後臺應用,採用這種方式能夠提效,下降開發難度和代碼維護難度。每一個頁面都是獨立的,系統再造也是極其簡單的。
上了Serverless以後,除了本地CLI構建外,還能夠提供雲構建。好比每一個頁面都是函數,那麼多個頁面如何合併呢?好比UMI典型的多頁應用,經過react-loadable+react-router實現的代碼按需加載,這種狀況就能夠先在Serverless上發佈多個頁面,繼而在雲構建上,配置多個頁面,最終構建成一個多頁應用。
另外,渲染層作輕,對於將來上Web IDE也是有極大便利的。能夠暢想一下,將來有個流程就能夠完成開發,是否是很美好?阿里將Serverless和IDE做爲兩大方向,確實是很是用心的考慮。
以後咱們來聊聊先後端協做。前端原本的定義是指基於瀏覽器作開發的,隨着瀏覽器應用範圍愈來愈廣,前端的界定也愈來愈模糊。目前前端已經涵蓋PC、H五、移動端組件,幾乎是全部和用戶接觸的帶界面的都算前端,因此纔有了大前端的概念,泛指全部和用戶交互的終端開發。
一直以來,跨瀏覽器(Web View)都是開發領域折騰的方向,從Native到Hybrid,到React Native/Weex,再到Electron、PWA、小程序,你能看到你們都在往輕量級走,統一技術棧,下降成本。也就是說,端上但願統一,又之前端爲開發核心,故而統稱爲大前端。
JavaScript已橫跨三端,再說一下Node,覆蓋工程化和服務端能力。不少大公司移動端和前端已經合併一個組進行管理,大前端局面已成定局。
先後端分離,即便用JavaScript運行在客戶端,經過請求獲取服務端接口數據,藉助如JQuery、Angular、React、Vue等前端框架操做或生成頁面DOM,充分利用客戶端資源,減小服務端壓力,先後端分工明確,一直到如今還是最經常使用的開發方式。
BFF(API Proxy),獨立的API層的出現緣由有不少:
移動端興起,你們都開始面向API開發,但卻忽略了大前端不僅只有移動端,還有PC Client和PC/H5 Web。主要是屏幕大小不一樣而致使UI/UE上有明顯差異。
後端開發API的時候,不喜歡同時維護多套API。
除了溝通成本,還有對前端實現不瞭解而產生的誤解。
簡單理解,BFF就是各端的API獨立提供了。可是,這樣作很明顯不經濟。那麼,是否是要把架構升級一下呢?
因而,分開的BFF變成了一個統一的服務器端API服務。這就和Ajax原理同樣,由於多了一層XHR,致使先後端能夠異步處理,促使了Web 2.0的誕生。BFF升級爲統一的服務器端API服務,其實也是同樣的,讓前端和後端能夠異步開發,各自作各自的工做,全部API都註冊到API層就行了。這實際上是前端架構上一次很是大的升級。
GraphQL是Facebook於2012年在內部開發的數據查詢語言,在2015年開源,旨在提供RESTful架構體系的替代方案,既是一種用於API的圖表化(可視化)查詢語言,也是一個知足你數據查詢的運行時。
傳統Web應用經過開發服務給客戶端提供接口是很常見的場景。而當需求或數據發生變化時,應用須要修改或者從新建立新的接口。長此之後,會形成服務器代碼的不斷增加,接口內部邏輯複雜難以維護。而GraphQL則經過如下特性解決這個問題:
聲明式。查詢的結果格式由請求方(即客戶端)決定而非響應方(即服務器端)決定。你不須要編寫不少額外的接口來適配客戶端請求。
可組合。GraphQL的查詢結構能夠自由組合來知足需求。
強類型。每一個GraphQL查詢必須遵循其設定的類型纔會被執行。
也就是說,經過以上的三個特性,當需求發生變化,客戶端只須要編寫能知足新需求的查詢結構,若是服務端能提供的數據知足需求,服務端代碼幾乎不須要作任何的修改。
GraphQL經過模型定義,對先後端都很是友好,無疑是一種很是好的解決方案。固然,它也有問題,約定自己就不是件容易的事兒,另外對於先後端成本仍是有的。
那麼,如何可以讓前端真正解放呢?一不關心運維,二不關係擴容,三不關心Web框架。對於前端開發者而言,我只是想要個接口,或者是包裝一個接口,爲何必定要了解Node Web框架呢?
Node.js成也Eventloop敗也Eventloop,自己Eventloop是黑盒,開發將什麼樣的代碼堆進去你是很難所有覆蓋的,偶爾會出現Eventloop阻塞的狀況,排查起來是極爲痛苦的。
利用Serverless,能夠有效防止Eventloop阻塞。好比加密是常見場景,但自己執行效率是很是慢的。若是加解密和你的其餘任務放到一塊兒,是很容易致使Eventloop阻塞的。
本地開發一個函數,而後經過CLI發佈到Serverless雲上,一切都那麼簡單,必然是將來趨勢。
能夠看下面這張圖。
CRA、UMI讓咱們不關心webpack配置,這是本地CLI的將來。
CSR和SSR統一,同構開發,本地CLI打包構建,發佈到Serverless雲上,簡單高效。
API基於Serverless極其簡單,不須要了解Web框架,簡單組成API便可,無運維,不怕高併發場景。
這裏要提一下,在Serverless這種大環境下,若是API解決了,前端天然是能夠作全部業務的。前端能作的事兒愈來愈多,將來前端應用層也會更多。
將來隨着經濟和業務發展,好比會有更多應用也會更多端,對前端來講是好事,由於能作的更多,價值就可以獲得很是好的體現。
因此做爲一個堅決的Web信仰者,我相信將來前端的前景一片大好。
原文語雀鏈接:www.yuque.com/robinson/fe…
若是你想了解更多關於前端工程化的文章,歡迎關注個人語雀專輯,持續收集整理中:www.yuque.com/robinson/fe…
申明:本文是在掘金平臺寫的最後一篇文章,之後都轉移到語雀和博客了。