這篇文章主要講解了“軟件架構(gòu)之如何理解前后端分離與前端模塊化”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“軟件架構(gòu)之如何理解前后端分離與前端模塊化”吧!
創(chuàng)新互聯(lián)建站是一家專注于成都做網(wǎng)站、網(wǎng)站制作與策劃設(shè)計(jì),張掖網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專注于網(wǎng)站建設(shè)10年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:張掖等地區(qū)。張掖做網(wǎng)站價(jià)格咨詢:13518219792
在正式說明前后臺架構(gòu)分離之前,我們來看一下多年之前,傳統(tǒng)軟件開發(fā)的架構(gòu)模式。
還記得零幾年我上大學(xué)的時(shí)候,在初學(xué) Java Web 開發(fā)時(shí),課本上介紹的還是 JSP + Servlet 這種很傳統(tǒng)的架構(gòu)模式,這時(shí)候前端和后端業(yè)務(wù)邏輯代碼都在一個(gè)工程里面,還沒有分離開來,這種開發(fā)模式屬于 Model1 模式,雖然實(shí)現(xiàn)了邏輯功能和顯示功能的分離,但是由于視圖層和控制層都是由 JSP 頁面實(shí)現(xiàn)的,即視圖層和控制層并沒有實(shí)現(xiàn)分離。
隨著學(xué)習(xí)的深入以及漸漸流行的企業(yè)應(yīng)用開發(fā),我們漸漸的擯棄這種技術(shù)選型,并開始在項(xiàng)目中使用了若干開源框架,常用的框架組合有 Spring +Struts/Spring MVC + Hibernate/Mybatis 等等,由于框架的優(yōu)越性以及良好的封裝性使得這套開發(fā)框架組合迅速成為各個(gè)企業(yè)開發(fā)中的不二之選,這些框架的出現(xiàn)也減少了開發(fā)者的重復(fù)編碼工作,簡化開發(fā),加快開發(fā)進(jìn)度,降低維護(hù)難度,隨之而火熱的是這套技術(shù)框架背后的開發(fā)模式,即 MVC 開發(fā)模式,它是為了克服 Model1 存在的不足而設(shè)計(jì)的。
MVC 的具體含義是:Model + View + Controller,即模型+視圖+控制器,
Model 模型層: 它常常使用 JavaBean 來編寫,它接受視圖層請求的數(shù)據(jù),然后進(jìn)行相應(yīng)的業(yè)務(wù)處理并返回最終的處理結(jié)果,它負(fù)擔(dān)的責(zé)任最為核心,并利用 JavaBean 具有的特性實(shí)現(xiàn)了代碼的重用和擴(kuò)展以及給維護(hù)帶來了方便。
View 視圖層: 代表和用戶交互的界面,負(fù)責(zé)數(shù)據(jù)的采集和展示,通常由 JSP 實(shí)現(xiàn)。
Controller 控制層: 控制層是從用戶端接收請求,然后將請求傳遞給模型層并告訴模型層應(yīng)該調(diào)用什么功能模塊來處理該請求,它將協(xié)調(diào)視圖層和模型層之間的工作,起到中間樞紐的作用,它一般交由 Servlet 來實(shí)現(xiàn)。
MVC的工作流程如下圖所示。
同時(shí),項(xiàng)目開發(fā)在進(jìn)行模塊分層時(shí)也會劃分為三層:控制層,業(yè)務(wù)層,持久層??刂茖迂?fù)責(zé)接收參數(shù),調(diào)用相關(guān)業(yè)務(wù)層,封裝數(shù)據(jù),以及路由并將數(shù)據(jù)渲染到 JSP 頁面,然后在 JSP 頁面中將后臺的數(shù)據(jù)展現(xiàn)出來,相信大家對這種開發(fā)模式都十分熟悉,不管是企業(yè)開發(fā)或者是個(gè)人項(xiàng)目的搭建,這種開發(fā)模式都是大家的首選,不過,隨著開發(fā)團(tuán)隊(duì)的擴(kuò)大和項(xiàng)目架構(gòu)的不斷演進(jìn),這套開發(fā)模式漸漸有些力不從心。
接下來,我們來分析下這套開發(fā)模式的痛點(diǎn)。
首先,JSP 必須要在 Servlet 容器中運(yùn)行(例如 Tomcat,jetty 等),在請求 JSP 時(shí)也需要進(jìn)行一次編譯過程,最后被譯成 Java 類和 class 文件,這些都會占用 PermGen 空間,同時(shí)也需要一個(gè)新的類加載器加載,JSP 技術(shù)與 Java 語言和 Servlet 有強(qiáng)關(guān)聯(lián),在解耦上無法與模板引擎或者純 html 頁面相媲美。其次每次請求 JSP 后得到的響應(yīng)都是 Servlet 通過輸出流輸出的 html 頁面,效率上也沒有直接使用 html 高。由于 JSP 與 Servlet 容器的強(qiáng)關(guān)聯(lián),在項(xiàng)目優(yōu)化時(shí)也無法直接使用 Nginx 作為 JSP 的 web 服務(wù)器,性能提升不高。
在這種開發(fā)模式下的工作流程通常是:設(shè)計(jì)人員給出頁面原型設(shè)計(jì)后,前端工程師只負(fù)責(zé)將設(shè)計(jì)圖切成 html 頁面,之后則需要由后端開發(fā)工程師來將 html 轉(zhuǎn)為 JSP 頁面進(jìn)行邏輯處理和數(shù)據(jù)展示。在這種工作模式下,人為出錯(cuò)率較高,后端開發(fā)人員任務(wù)更重,修改問題時(shí)需要雙方協(xié)同開發(fā),效率低下,一旦出現(xiàn)問題后,前端開發(fā)人員面對的是充滿標(biāo)簽和表達(dá)式的 JSP 頁面,后端人員在面對樣式或者交互的問題時(shí)本就造詣不高的前端技術(shù)也會捉襟見肘。
在某些緊急情況下也會出現(xiàn)前端人員調(diào)試后端代碼,后端開發(fā)人員調(diào)試前端代碼這些讓人捧腹的現(xiàn)象,分工不明確,且溝通成本大,一旦某些功能需要返工則需要前后端開發(fā)人員,這種情況下,對于前后端人員的后期技術(shù)成長也不利,后端追求的是高并發(fā)、高可用、高性能、安全、架構(gòu)優(yōu)化等,前端追求的是模塊化、組件整合、速度流暢、兼容性、用戶體驗(yàn)等等,但是在 MVC 這種開發(fā)模式下顯然會對這些技術(shù)人員都有一定的掣肘。
項(xiàng)目初期,為了快速上線應(yīng)用,選擇使用這種開發(fā)模式來進(jìn)行 Java Web 項(xiàng)目的開發(fā)是非常正確的選擇,此時(shí)流量不大,用戶量也不高,并不會有非??量痰男阅芤螅请S著項(xiàng)目的不斷成長,用戶量和請求壓力也會不斷擴(kuò)大,對于互聯(lián)網(wǎng)項(xiàng)目的性能要求是越來越高,如果此時(shí)的前后端模塊依舊耦合在一起是非常不利于后續(xù)擴(kuò)展的。舉例說明一下,為了提高負(fù)載能力,我們會選擇做集群來分擔(dān)單個(gè)應(yīng)用的壓力,但是模塊的耦合會使得性能的優(yōu)化空間越來越低,因?yàn)閱蝹€(gè)項(xiàng)目會越來越大,不進(jìn)行合理的拆分無法做到最好的優(yōu)化,又或者在發(fā)版部署上線的時(shí)候,明明只改了后端的代碼,前端也需要重新發(fā)布,或者明明只改了部分頁面或者部分樣式,后端代碼也需要一起發(fā)布上線,這些都是耦合較嚴(yán)重時(shí)常見的不良現(xiàn)象,因此原始的前后端耦合在一起的架構(gòu)模式已經(jīng)逐漸不能滿足項(xiàng)目的演進(jìn)方向,需要需找一種解耦的方式替代當(dāng)前的開發(fā)模式。
隨著公司業(yè)務(wù)的不斷發(fā)展,僅僅只有瀏覽器端的 Web 應(yīng)用已經(jīng)逐漸顯得有些不夠用了,目前又是移動互聯(lián)網(wǎng)急劇增長的時(shí)代,手機(jī)端的原生 App 應(yīng)用已經(jīng)非常成熟,隨著 App 軟件的大量普及越來越多的企業(yè)也加入到 App 軟件開發(fā)當(dāng)中來,為了盡可能的搶占商機(jī)和提升用戶體驗(yàn),你所在的公司可能也不會把所有的開發(fā)資源都放在 web 應(yīng)用上,而是多端應(yīng)用同時(shí)開發(fā),此時(shí)公司的業(yè)務(wù)線可能就是如下的幾種或者其中一部分:
瀏覽器端的 Web 應(yīng)用、iOS 原生 App、安卓端原生 App、微信小程序等等,可能只是開發(fā)其中的一部分產(chǎn)品,但是除了 web 應(yīng)用能夠使用傳統(tǒng)的 MVC 模式開發(fā)外,其他的都無法使用該模式進(jìn)行開發(fā),像原生 App 或者微信小程序都是通過調(diào)用 RESTful api 的方式與后端進(jìn)行數(shù)據(jù)交互。
隨著互聯(lián)網(wǎng)技術(shù)的發(fā)展,更多的技術(shù)框架被提了出來,其中最革命性的就是前后端分離概念的提出。
何為前后端分離,我認(rèn)為應(yīng)該從以下幾個(gè)方面來理解。
當(dāng)業(yè)務(wù)變得越來越復(fù)雜或者產(chǎn)品線越來越多,原有的開發(fā)模式已經(jīng)無法滿足業(yè)務(wù)需求,當(dāng)端上的產(chǎn)品越來越多,展現(xiàn)層的變化越來越快、越來越多,此時(shí)就應(yīng)該進(jìn)行前后端分離分層抽象,簡化數(shù)據(jù)獲取過程,比如目前比較常用的就是前端人員自行實(shí)現(xiàn)跳轉(zhuǎn)邏輯和頁面交互,后端只負(fù)責(zé)提供接口數(shù)據(jù),二者之間通過調(diào)用 RESTful api 的方式來進(jìn)行數(shù)據(jù)交互,如下圖所示:
此時(shí)就不會出現(xiàn) HTML 代碼需要轉(zhuǎn)成 JSP 進(jìn)行開發(fā)的情況,前端項(xiàng)目只負(fù)責(zé)前端部分,并不會摻雜任何后端代碼,這樣的話代碼不再耦合。同時(shí),前端項(xiàng)目與后端項(xiàng)目也不會再出現(xiàn)耦合嚴(yán)重的現(xiàn)象,只要前后端協(xié)商和定義好接口規(guī)范及數(shù)據(jù)交互規(guī)范,雙方就可以并行開發(fā),互不干擾,業(yè)務(wù)也不會耦合,兩端只通過接口來進(jìn)行交互。
在 MVC 模式開發(fā)項(xiàng)目時(shí),往往后端過重,“控制權(quán)”也比較大,既要負(fù)責(zé)處理業(yè)務(wù)邏輯、權(quán)限管理等后端操作,也需要處理頁面跳轉(zhuǎn)等邏輯,在前后端分離的模式中,后端由原來的大包大攬似的獨(dú)裁者變成了接口提供者,而前端也不僅僅是原來那樣僅處理小部分業(yè)務(wù),頁面跳轉(zhuǎn)也不再由后端來處理和決定,整個(gè)項(xiàng)目的控制權(quán)已經(jīng)由后端過渡至前端來掌控,前端需要處理的更多。
前端項(xiàng)目和后端項(xiàng)目隔離開來、互不干涉,通過接口和數(shù)據(jù)規(guī)范來完成項(xiàng)目功能需求,這也是目前比較流行的一種開發(fā)方式。
在前后端分離的架構(gòu)模式下,后臺負(fù)責(zé)數(shù)據(jù)提供,前端負(fù)責(zé)顯示交互,在這種開發(fā)模式下,前端開發(fā)人員和后端開發(fā)人員分工明確,職責(zé)劃分十分清晰,雙方各司其職,不會存在邊界不清晰的地方,并且從業(yè)人員也各司其職。
前端開發(fā)人員包括 Web 開發(fā)人員、原生 App 開發(fā)人員,后端開發(fā)則是指 Java 開發(fā)人員(以 Java 語言為例),不同的開發(fā)人員只需要注重自己所負(fù)責(zé)的項(xiàng)目即可。后端專注于控制層(RESTful API)、服務(wù)層 、數(shù)據(jù)訪問層,前端專注于前端控制層、 視圖層,不會再出現(xiàn)前端人員需要維護(hù)部分后端代碼,或者后端開發(fā)人員需要去調(diào)試樣式等等職責(zé)不清和前后端耦合的情況,我們通過兩張項(xiàng)目開發(fā)流程簡圖來對比:
此時(shí),開發(fā)過程中會存在前后端耦合的情況,如果出現(xiàn)問題前端需要返工、后端也需要返工,開發(fā)效率會有所影響?,F(xiàn)在,前后端分離后流程簡圖如下:
前后端分離后,服務(wù)器端開發(fā)人員和前端開發(fā)人員各干各的,大家互不干擾,。在設(shè)計(jì)完成后,Web 端開發(fā)人員、App 端開發(fā)人員、后端開發(fā)人員都可以投入到開發(fā)工作當(dāng)中,能夠做到并行開發(fā),前端開發(fā)人員與后端開發(fā)人員職責(zé)分離,即使出現(xiàn)問題,也是修復(fù)各自的問題不會互相影響和耦合,開發(fā)效率高且滿足企業(yè)對于多產(chǎn)品線的開發(fā)需求。
前后端分離后,各端應(yīng)用可以獨(dú)立打包部署,并針對性的對部署方式進(jìn)行優(yōu)化,不再是前后端一個(gè)統(tǒng)一的工程最終打成一個(gè)部署包進(jìn)行部署。以 Web 應(yīng)用為例,前端項(xiàng)目部署后,不再依賴于 Servlet 容器,可以使用吞吐量更大的 Nginx 服務(wù)器,采用動靜分離的部署方式,既提升了前端的訪問體驗(yàn),也減輕了后端服務(wù)器的壓力,再進(jìn)一步優(yōu)化的話,可以使用頁面緩存、瀏覽器緩存等設(shè)置,也可以使用 cdn 等產(chǎn)品提升靜態(tài)資源的訪問效率。對于后端服務(wù)而言,可以進(jìn)行集群部署提升服務(wù)的響應(yīng)效率,也可以進(jìn)一步的進(jìn)行服務(wù)化的拆分等等。前后端分離后的獨(dú)立部署維護(hù)以及針對性的優(yōu)化,可以加快整體響應(yīng)速度和吞吐量。
當(dāng)我們?nèi)チ私饽硞€(gè)事物的時(shí)候,首先我們需要去了解它的歷史,才能更好的把握它的未來。
世界上第一款瀏覽器 NCSAMosaic ,是網(wǎng)景公司(Netscape)在1994年開發(fā)出來的,它的初衷是為了方便科研人員查閱資料、文檔(這個(gè)時(shí)候的文檔大多是圖片形式的)。那個(gè)時(shí)代的每一個(gè)交互,按鈕點(diǎn)擊、表單提交,都需要等待瀏覽器響應(yīng)很長時(shí)間,然后重新下載一個(gè)新頁面。
同年 PHP(超文本預(yù)處理器) 腳本語言被開發(fā)出來,開啟了數(shù)據(jù)嵌入模板的 MVC 模式,同時(shí)期比較類似的做法有以下幾種:
PHP 直接將數(shù)據(jù)內(nèi)嵌到 HTML 中。
ASP 的 ASPX,在 HTML 中嵌入 C# 代碼。
Java 的 JSP 直接將數(shù)據(jù)嵌入到網(wǎng)頁中。
這個(gè)時(shí)期,瀏覽器的開發(fā)者,以后臺開發(fā)人員居多,大部分前后端開發(fā)是一體的,大致開發(fā)流程是:后端收到瀏覽器的請求 ---> 發(fā)送靜態(tài)頁面 ---> 發(fā)送到瀏覽器。即使是有專門的前端開發(fā),也只是用 HTML 寫寫頁面模板、CSS 給頁面排個(gè)好看點(diǎn)的版式。在這一時(shí)期,前端的作用有限,往往只是切圖仔的角色。
1995年,網(wǎng)景公司的一位叫布蘭登·艾奇的大佬,希望開發(fā)出一個(gè)類似 Java 的腳本語言,用來提升瀏覽器的展示效果,增強(qiáng)動態(tài)交互能力。結(jié)果大佬喝著啤酒抽著煙,十來天就把這個(gè)腳本語言寫出來了,功能很強(qiáng)大,就是語法一點(diǎn)都不像 Java。這樣就漸漸形成了前端的雛形:HTML 為骨架,CSS 為外貌,JavaScript 為交互。
同時(shí)期微軟等一些公司也針對自家瀏覽器開發(fā)出了自己的腳本語言。瀏覽器五花八門,雖然有了比較統(tǒng)一的 ECMA 標(biāo)準(zhǔn),但是瀏覽器先于標(biāo)準(zhǔn)在市場上流行開來,成為了事實(shí)標(biāo)準(zhǔn)。導(dǎo)致,現(xiàn)在前端工程師還要在做一些政府古老項(xiàng)目的時(shí)候,還要去處理瀏覽器兼容(萬惡的 IE 系列)。
不管怎么說,前端開發(fā)也算是能寫點(diǎn)邏輯代碼了,不再是只能畫畫頁面的低端開發(fā)了。隨著1998年 AJax 的出現(xiàn),前端開發(fā)從 Web1.0邁向了Web2.0,前端從純內(nèi)容的靜態(tài)展示,發(fā)展到了動態(tài)網(wǎng)頁,富交互,前端數(shù)據(jù)處理的新時(shí)期。這一時(shí)期,比較知名的兩個(gè)富交互動態(tài)的瀏覽器產(chǎn)品是。
Gmail(2004年)
Google 地圖(2005年)
由于動態(tài)交互、數(shù)據(jù)交互的需求增多,還衍生出了jQuery(2006) 這樣優(yōu)秀的跨瀏覽器的 js 工具庫,主要用于 DOM 操作,數(shù)據(jù)交互。有些古老的項(xiàng)目,甚至近幾年開發(fā)的大型項(xiàng)目現(xiàn)在還在使用 jQuery,以至于 jQuery 庫現(xiàn)在還在更新,雖然體量上已經(jīng)遠(yuǎn)遠(yuǎn)不及 React、Vue 這些優(yōu)秀的前端庫。
自 2003 以后,前端發(fā)展渡過了一段比較平穩(wěn)的時(shí)期,各大瀏覽器廠商除了按部就班的更新自己的瀏覽器產(chǎn)品之外,沒有再作妖搞點(diǎn)其他事情。但是我們程序員們耐不住寂寞啊,工業(yè)化推動了信息化的快速到來,瀏覽器呈現(xiàn)的數(shù)據(jù)量越來越大,網(wǎng)頁動態(tài)交互的需求越來越多,JavaScript 通過操作 DOM 的弊端和瓶頸越來越明顯(頻繁的交互操作,導(dǎo)致頁面會很卡頓),僅僅從代碼層面去提升頁面性能,變得越來越難。于是優(yōu)秀的大佬們又干了點(diǎn)驚天動地的小事兒:
2008 年,谷歌 V8 引擎發(fā)布,終結(jié)微軟 IE 時(shí)代。
2009 年 AngularJS 誕生、Node誕生。
2011 年 ReactJS 誕生。
2014 年 VueJS 誕生。
其中,V8 和 Node.JS 的出現(xiàn),使前端開發(fā)人員可以用熟悉的語法糖編寫后臺系統(tǒng),為前端提供了使用同一語言的實(shí)現(xiàn)全棧開發(fā)的機(jī)會(JavaScript不再是一個(gè)被嘲笑只能寫寫頁面交互的腳本語言)。React、Angular、Vue 等 MVVM 前端框架的出現(xiàn),使前端實(shí)現(xiàn)了項(xiàng)目真正的應(yīng)用化(SPA單頁面應(yīng)用),不再依賴后臺開發(fā)人員處理頁面路由 Controller,實(shí)現(xiàn)頁面跳轉(zhuǎn)的自我管理。同時(shí)也推動了前后端的徹底分離(前端項(xiàng)目獨(dú)立部署,不再依賴類似的 template 文件目錄)。
至于為啥 MVVM 框架能提升前端的渲染性能,這里簡單的說一下原理,因?yàn)榇罅康?DOM 操作是性能瓶頸的罪魁禍?zhǔn)?,那通過一定的分析比較算法,實(shí)現(xiàn)同等效果下的最小 DOM 開銷是可行的。React、Vue 這類框架大都是通過這類思想實(shí)現(xiàn)的,具體實(shí)現(xiàn)可以去看一下相關(guān)資料。前后端分離也導(dǎo)致前端的分工發(fā)生了一些變化。
而后端開發(fā)更加關(guān)注數(shù)據(jù)服務(wù),前端則負(fù)責(zé)展示和交互。當(dāng)然相應(yīng)的學(xué)習(xí)成本也越來越大,Node.JS的出現(xiàn)也使得前端前后端一起開發(fā)成為可能,好多大公司在 2015 年前后就進(jìn)行了嘗試,用 Node.JS 作為中間數(shù)據(jù)轉(zhuǎn)接層,讓后端更加專注于數(shù)據(jù)服務(wù)和治理。
自 2009 年 5 月 Node.js 發(fā)布以來,前端能干的事情越來越多。短短 10 來年的時(shí)間,前端便從刀耕火種的年代走向了模塊化、工程化的時(shí)代。各種前端框架百家爭鳴,前端贏來了真正屬于自己的時(shí)代。
時(shí)間回到 2009年,記得那時(shí)候還沒有流行前后端分離,很多項(xiàng)目還是混在一起,而那時(shí)候的前端開發(fā)人員大多數(shù)也都是“切圖仔”。前端完成靜態(tài)頁面,由服務(wù)端同事完成數(shù)據(jù)的嵌入,也就是所謂的套頁面操作,每當(dāng)有類似的功能,都會回到之前的頁面去復(fù)制粘貼,由于處于不同的頁面,類名需要更換,但是換湯不換藥。
久而久之,重復(fù)代碼越來越多,但凡改動一個(gè)小的地方,都需要改動很多代碼,顯得極不方便,也不利于大規(guī)模的進(jìn)行工程化開發(fā)。雖然市面上也慢慢出現(xiàn)了 Angular、 Avalon 等優(yōu)秀的前端框架,但是考慮到 SEO 和維護(hù)人員并不好招,很多公司還是選擇求穩(wěn),用套頁面的形式制作網(wǎng)頁,這對前端的工程化、模塊化是一個(gè)不小的阻礙。
不過,隨著 Node 被大力推崇,市面上涌現(xiàn)出大量的構(gòu)建工具,如 Npm Scripts、Grunt、Gulp、FIS、Webpack、Rollup、Parcel等等。構(gòu)建工具解放了我們的雙手,幫我們處理一些重復(fù)的機(jī)械勞動。
舉個(gè)簡單的例子:我們用 ES6 寫了一段代碼,需要在瀏覽器執(zhí)行。但是由于瀏覽器廠商對瀏覽器的更新非常保守,使得很多 ES6 的代碼并不能直接在瀏覽器上運(yùn)行。這個(gè)時(shí)候我們總不能手動將 ES6 代碼改成 ES5 的代碼。于是乎就有了下面的轉(zhuǎn)換。
//編譯前 [1,2,3].map(item => console.log(item)) //編譯后 [1, 2, 3].map(function (item) { return console.log(item); }); //代碼壓縮后 [1,2,3].map(function(a){return console.log(a)});
就是做了上述的操作,才能使得我們在寫前端代碼的時(shí)候,使用最新的 ECMAScript 語法,并且盡可能的壓縮代碼的體積,使得瀏覽器加載靜態(tài)腳本時(shí)能更加快速。
隨著 Ajax 的流行,前端工程師能做的事情就不只是“切圖” 這么簡單,現(xiàn)在前端工程師能做的越來越多,開始出現(xiàn)了明確的分工,并且能夠與服務(wù)端工程師進(jìn)行數(shù)據(jù)聯(lián)調(diào)。這里說的傳統(tǒng)模塊化還不是后現(xiàn)代的模塊化,早期的模塊化是不借助任何工具的,純屬由 JavaScript 完成代碼的結(jié)構(gòu)化。在傳統(tǒng)的模塊化中我們主要是將一些能夠復(fù)用的代碼抽成公共方法,以便統(tǒng)一維護(hù)和管理,比如下面代碼。
function show(id) { document.getElementById(id).setAttribute('style', "display: block") } function hide(id) { document.getElementById(id).setAttribute('style', "display: none") }
然后,我們將這些工具函數(shù)封裝到一個(gè) JS 腳本文件里,在需要使用它們的地方進(jìn)行引入。
但是,這種做法會衍生出兩個(gè)很大的問題,一個(gè)是全局變量的污染,另一個(gè)是人工維護(hù)模塊之間的依賴關(guān)系會造成代碼的混亂。
例如,當(dāng)我們的項(xiàng)目有十幾個(gè)甚至幾十個(gè)人維護(hù)的時(shí)候,難免會有人在公用組件中添加新的方法,比如 show 這個(gè)方法一旦被覆蓋了,使用它的人會得到和預(yù)期不同的結(jié)果,這樣就造成的全局變量的污染。另一個(gè)問題,因?yàn)檎鎸?shí)項(xiàng)目中的公用腳本之間的依賴關(guān)系是比較復(fù)雜的,比如 c 腳本依賴 b 腳本,a 腳本依賴 b 腳本,那么我們在引入的時(shí)候就要注意必須要這樣引入。
要這樣引入才能保證 a 腳本的正常運(yùn)行,否則就會報(bào)錯(cuò)。對于這類問題,我們該如何解決這樣的問題呢?
解決這個(gè)問題有兩種,先說說治標(biāo)不治本的方法,我們通過團(tuán)隊(duì)規(guī)范開發(fā)文檔,比如說我有個(gè)方法,是在購物車模塊中使用的,可以如下書寫。
var shop.cart.utils = { show: function(id) { document.getElementById(id).setAttribute('style', "display: block") }, hide: function(id) { document.getElementById(id).setAttribute('style', "display: none") } }
這樣就能比較有效的避開全局變量的污染,把方法寫到對象里,再通過對象去調(diào)用。專業(yè)術(shù)語上這叫命名空間的規(guī)范,但是這樣模塊多了變量名會比較累贅,一寫就是一長串,所以我叫它治標(biāo)不治本。
還有一種比較專業(yè)的方法技術(shù)通過立即執(zhí)行函數(shù)完成閉包封裝,為了解決封裝內(nèi)變量的問題,立即執(zhí)行函數(shù)是個(gè)很好的辦法,這也是早期很多開發(fā)正在使用的方式,如下所示。
(function() { var Cart = Cart || {}; function show (id) { document.getElementById(id).setAttribute('style', "display: block") } function hide (id) { document.getElementById(id).setAttribute('style', "display: none") } Cart.Util = { show: show, hide: hide } })();
上述代碼,通過一個(gè)立即執(zhí)行函數(shù),給予了模塊的獨(dú)立作用域,同時(shí)通過全局變量配置了我們的模塊,達(dá)到了模塊化的目的。
先來說說 CommonJS 規(guī)范,在 Node.JS 發(fā)布之后,CommonJS 模塊化規(guī)范就被用在了項(xiàng)目開發(fā)中,它有幾個(gè)概念給大家解釋一下。
每個(gè)文件都是一個(gè)模塊,它都有屬于自己的作用域,內(nèi)部定義的變量、函數(shù)都是私有的,對外是不可見的;
每個(gè)模塊內(nèi)部的 module 變量代表當(dāng)前模塊,這個(gè)變量是一個(gè)對象;
module 的 exports 屬性是對外的接口,加載某個(gè)模塊其實(shí)就是在加載模塊的 module.exports 屬性;
使用 require 關(guān)鍵字加載對應(yīng)的模塊,require 的基本功能就是讀入并執(zhí)行一個(gè) JavaScript 文件,然后返回改模塊的 exports 對象,如果沒有的話會報(bào)錯(cuò)的;
下面來看一下示例,我們就將上面提到過的代碼通過 CommonJS 模塊化。
module.exports = { show: function (id) { document.getElementById(id).setAttribute('style', "display: block") }, hide: function (id) { document.getElementById(id).setAttribute('style', "display: none") } } // 也可以輸出單個(gè)方法 module.exports.show = function (id) { document.getElementById(id).setAttribute('style', "display: block") } // 引入的方式 var utils = require('./utils') // 使用它 utils.show("body")
除了 CommonJS 規(guī)范外,還有幾個(gè)現(xiàn)在只能在老項(xiàng)目里才能看到的模塊化模式,比如以 require.js 為代表的 AMD(Asynchronous Module Definition) 規(guī)范 和 玉伯團(tuán)隊(duì)寫的 sea.js 為代表的 CMD(Common Module Definition) 規(guī)范。
AMD 的特點(diǎn):是一步加載模塊,但是前提是一開始就要將所有的依賴項(xiàng)加載完全。CMD 的特點(diǎn)是:依賴延遲,在需要的時(shí)候才去加載。
首先,我們來看一下如何通過 AMD 規(guī)范的 require.js 書寫上述模塊化代碼。
define(['home'], function(){ function show(id) { document.getElementById(id).setAttribute('style', "display: block") } function hide(id) { document.getElementById(id).setAttribute('style', "display: none") } return { show: show, hide: hide }; }); // 加載模塊 require(['utils'], function (cart){ cart.show('body'); });
require.js 定義了一個(gè)函數(shù) define,它是全局變量,用來定義模塊,它的語法規(guī)范如下:
define(id, dependencies, factory)
id:它是可選參數(shù),用于標(biāo)識模塊;
dependencies:當(dāng)前模塊所依賴的模塊名稱數(shù)組,如上述模塊依賴 home 模塊,這就解決了之前說的模塊之間依賴關(guān)系換亂的問題,通過這個(gè)參數(shù)可以將前置依賴模塊加載進(jìn)來;
factory:模塊初始化要執(zhí)行的函數(shù)或?qū)ο蟆?/p>
require([dependencies], function(){})
然后,在其他文件中使用 require 進(jìn)行引入,第一個(gè)參數(shù)為需要依賴的模塊數(shù)組,第二個(gè)參數(shù)為一個(gè)回調(diào)函數(shù),當(dāng)前面的依賴模塊被加載成功之后,回調(diào)函數(shù)會被執(zhí)行,加載進(jìn)來的模塊將會以參數(shù)的形式傳入函數(shù)內(nèi),以便進(jìn)行其他操作。
感謝各位的閱讀,以上就是“軟件架構(gòu)之如何理解前后端分離與前端模塊化”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對軟件架構(gòu)之如何理解前后端分離與前端模塊化這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!