來(lái)源:轉(zhuǎn)載 發(fā)布時(shí)間:2018-11-22 14:42:30 閱讀量:1542
0、前注
鑒于作者本人的能力有限(非常有限),并且依然在學(xué)習(xí)中,因此本文的高度和深度必然有所欠缺。
歡迎(并且非常歡迎)大家來(lái)批評(píng)指正,如果能詳細(xì)的說(shuō)明問(wèn)題在哪里,如何解決和改正,那么就太感謝了!??!
我最喜歡聽(tīng)有理有據(jù)的批評(píng)了??!
本人QQ:20004604,郵箱:20004604@qq.com,期待你的交流。
1、為什么要有一個(gè)好的架構(gòu)
首先明確一點(diǎn),架構(gòu)是為需求服務(wù)的。
前端架構(gòu)存在的目的,就我個(gè)人理解來(lái)說(shuō),有以下幾點(diǎn):
1、提高代碼的可讀性。
一個(gè)好的架構(gòu),代碼的可讀性一定是很強(qiáng)的。
簡(jiǎn)單來(lái)說(shuō),假如有一個(gè)新人加入團(tuán)隊(duì),那么他接手這個(gè)項(xiàng)目,一定是容易上手的,能簡(jiǎn)單輕松的了解整個(gè)前端部分的相互關(guān)系,從而找到自己需要重點(diǎn)關(guān)注的點(diǎn)。而不是需要花很多時(shí)間去熟悉這個(gè)項(xiàng)目的很多細(xì)節(jié),才能開(kāi)始上手做東西。
就文件來(lái)說(shuō),可以從文件名上,分清哪些是頁(yè)面、哪些是邏輯、哪些是樣式、哪些是可以復(fù)用的組件、哪些是圖標(biāo)組、又有哪些是移動(dòng)端或是PC端專享的樣式/邏輯等。
就代碼來(lái)說(shuō),包括統(tǒng)一的命名風(fēng)格,封裝在同一個(gè)文件里的代碼的相關(guān)性足夠強(qiáng)等。
2、提高代碼的可維護(hù)性。
一個(gè)好的架構(gòu),一定是易于維護(hù)的,例如在新增需求、更改需求、修正bug,都不會(huì)造成意料之外的變化,比如說(shuō)修改了一個(gè)頁(yè)面組件的內(nèi)容,卻導(dǎo)致另外一個(gè)頁(yè)面組件發(fā)生變化(這也太坑了)。
因此,要低耦合,高內(nèi)聚,以及輸入和輸出是可預(yù)期的。
3、提高代碼的可擴(kuò)展性。
一個(gè)好的架構(gòu),一定擴(kuò)展性要強(qiáng),不能寫(xiě)死。
需求變更太TM正常了,新增需求也太TM正常了。因此好的架構(gòu),必須要考慮到這些情況的發(fā)生,因?yàn)樗麄兪且欢〞?huì)發(fā)生的。
所以,一定要避免把代碼寫(xiě)死。
比如頁(yè)面組件A里需要有一個(gè)日歷組件,而這個(gè)日歷組件引用的是別人的(比如從github上找的)。
那么盡量不要直接在頁(yè)面組件A里面直接引用這個(gè)日歷組件,而是將寫(xiě)一個(gè)日歷組件B,在這個(gè)日歷組件B里封裝你引用的日歷組件C,然后通過(guò)這個(gè)日歷組件B來(lái)進(jìn)行操作。
原因很簡(jiǎn)單,假如某天產(chǎn)品經(jīng)理說(shuō),這個(gè)日歷組件太丑了,我們換一個(gè)吧。如果你直接在頁(yè)面組件A里內(nèi)嵌這個(gè)引用的日歷組件C,你很可能就要改很多代碼(因?yàn)椴煌諝v組件的使用方法和暴露的接口可能不同)。假如你還在其他多個(gè)地方引用了這個(gè)日歷組件,那就更糟糕了!每個(gè)地方都要改。
而若是將引用的日歷組件C封裝到自己寫(xiě)的日歷組件B之中,那么你只需要改日歷組件B里的相應(yīng)代碼即可,而因?yàn)槿諝v組件B暴露的接口是不變的,那么自然不用修改頁(yè)面中的代碼了。
附圖,以日歷組件為例,是否考慮到擴(kuò)展性的結(jié)果
未考慮到擴(kuò)展性
考慮到擴(kuò)展性:
4、便于協(xié)同。
包括前端和后端的協(xié)同,前端和前端之間的協(xié)同。
具體來(lái)說(shuō),前后端的協(xié)同通常是以ajax為交互,那么應(yīng)至少有一個(gè)用于專門(mén)封裝了所有ajax請(qǐng)求的文件,所有ajax請(qǐng)求都封裝在這里。在開(kāi)發(fā)時(shí),這里封裝的方法應(yīng)該可以模擬發(fā)送和接收約定好的交互內(nèi)容,方便開(kāi)發(fā)聯(lián)調(diào)。
而前端和前端的協(xié)同,主要體現(xiàn)在同時(shí)在更改代碼時(shí),不會(huì)影響對(duì)方代碼的正常運(yùn)行。因此要求封裝、解耦以及低干擾度是必須的。
5、自動(dòng)化
自動(dòng)打包,壓縮,混淆,如果有必要,再加上自動(dòng)單元測(cè)試。
總結(jié):
總結(jié)來(lái)說(shuō),一個(gè)好的架構(gòu)的目的是,讓前端寫(xiě)代碼寫(xiě)的舒服,讓后端聯(lián)調(diào)的舒服,讓產(chǎn)品經(jīng)理改需求改的舒服。
2、我如何設(shè)計(jì)架構(gòu)
我不敢說(shuō)自己的架構(gòu)是好的架構(gòu)(顯然不是啦),只能分享自己最近做的一個(gè)項(xiàng)目,它的架構(gòu)的如何做的。
首先,確定需求:
1、一個(gè)中小型網(wǎng)站,同時(shí)面向移動(dòng)端和PC端(單端大概15個(gè)頁(yè)面,算上彈窗大約20個(gè))。
2、預(yù)算有限(給的錢(qián)少),開(kāi)發(fā)時(shí)間有限(一個(gè)月)。
3、可能存在一定程度上的需求變更(比如增加頁(yè)面或修改某些頁(yè)面內(nèi)容)。
4、客戶可能不太在乎優(yōu)化(但是我自己在乎啊?。?/span>
5、要求兼容IE9以上。
其次開(kāi)始決定:
1、兼容IE9以上說(shuō)明可以使用主流框架,而無(wú)需必須使用jquery。因此我采用了vue,版本是2.0;
2、預(yù)算有限,時(shí)間有限,因此PC端和移動(dòng)端共html和js,獨(dú)立css。
3、頁(yè)面有限,因此無(wú)需將架構(gòu)層級(jí)劃分的比較細(xì),只需要按其類型劃分即可;
4、根據(jù)原型圖來(lái)看,頁(yè)面復(fù)雜程度有限,復(fù)用部分不是很多,因此可以確定哪些東西需要封裝復(fù)用,哪些比較復(fù)雜需要獨(dú)立封裝,哪些比較簡(jiǎn)單為了簡(jiǎn)化開(kāi)發(fā)難度可以直接耦合。
5、自己比較熟練單頁(yè)面網(wǎng)站,因此采用以單頁(yè)面為主,異步加載其他頁(yè)面的形式。
于是使用相關(guān)配套的東西,比如webpack,vue-router等,另外為了開(kāi)發(fā)和生產(chǎn)的方便性,采用以下模式進(jìn)行開(kāi)發(fā)。
第三,劃分功能。
首先有一個(gè)根html,用戶需要通過(guò)訪問(wèn)它來(lái)加載我們的js邏輯,因此js邏輯的代碼被寫(xiě)在main.js之中。
在main.js之下,我們的前端代碼可以被劃分為三部分:
組件樹(shù);
功能模塊;
各種資源;
如下圖:
功能劃分好之后,相同功能的放在同一個(gè)文件夾下,命名風(fēng)格應(yīng)該類似。
具體來(lái)說(shuō),組件樹(shù)相關(guān)的東西,通常是以.vue結(jié)尾,放置在components文件夾下;
資源,有圖片或者國(guó)際化資源等,以.png或者.js或.json結(jié)尾,放置在resources文件夾下;
而功能插件、服務(wù)等,因?yàn)榭赡鼙欢嗵幰?,因此為了方便引用,放在src文件夾下,并且該文件夾是components文件夾和resources文件夾的上級(jí)文件夾;
第四、細(xì)化功能模塊
功能、組件樹(shù)以及資源,我們已經(jīng)明確了有哪些東西,那么接下來(lái),我們要明確這些東西該如何以文件的形式來(lái)劃分。
如下圖:
1、項(xiàng)目構(gòu)建相關(guān)
因?yàn)橐褂胿ue.js,也要使用es6語(yǔ)法,因此babel是必須的;
又因?yàn)橐詣?dòng)化混淆打包,因此webpack也是必須的;
最后因?yàn)橐奖愣嗳藚f(xié)同,因此npm的package.json的配置,方便不同人可以快速自動(dòng)化通過(guò)npm install來(lái)安裝依賴,也是必須的;
2、CDN相關(guān)
而又因?yàn)槲覀円捎猛獠孔煮w(需求要求引入非常見(jiàn)字體),因此CDN加速是必須的,該字體文件放在html中來(lái)配置引用即可;
3、配置和插件
我們需要直接引入一些插件和配置文件;
為了使用vue,我們需要一個(gè)根組件,那么就是App.vue;
使用vue-router,我們需要配置路由文件,因此router-config.js這個(gè)路由配置也是必須的;
然后我們還需要以插件形式引入一些功能和服務(wù),因此有了Plugin-開(kāi)頭的若干個(gè)vue插件,這些都是根據(jù)需要封裝好的低耦合高內(nèi)聚方法;
4、需要的npm依賴
當(dāng)然,要使用vue肯定要引入vue.js;
類似的還有vue-router.js和各種兼容性polyfill和全局插件;
5、抽離出的功能模塊
除了直接引用的這些插件,我們還有一些和項(xiàng)目高度耦合的功能服務(wù),我認(rèn)為不能作為插件,但依然需要抽離出來(lái)封裝好,方便使用和修改;
如封裝ajax請(qǐng)求的ajax.js,所有的ajax請(qǐng)求都放置其中,只對(duì)外暴露接口,方便管理和使用;
又如實(shí)時(shí)國(guó)際化功能的組件LanguageManager.js,他需要引入國(guó)際化資源和管理國(guó)際化資源的加載;
又例如實(shí)現(xiàn)跨組件通信的event-bus.js
又比如管理用戶信息的user.js
6、總結(jié)
而這些劃分,都體現(xiàn)在上圖之中。這就是src目錄下的功能模塊文件,我們需要的絕大多數(shù)功能都可以包括在其中,我們只需要按照實(shí)際開(kāi)發(fā)中的需要,將對(duì)應(yīng)的功能寫(xiě)入在這些文件中并引用即可;
第五、組件樹(shù)
之前談了功能模塊的劃分,接下來(lái)是組件樹(shù);
因此是中小型頁(yè)面,因此組件樹(shù)的層級(jí)無(wú)需太深,但該抽離出來(lái)的依然還是要抽離,盡量保證抽離出來(lái)的組件解耦以及一個(gè)頁(yè)面組件的邏輯不要太多;
如下圖:
0、根組件
所有組件最終往上找,都會(huì)找到共同的根組件App.vue,根組件只負(fù)責(zé)管理他的直接子組件;
每個(gè)組件都只負(fù)責(zé)管理自己的直接子組件,不跨級(jí)管理,并且不依賴于自己的子組件(否則可能因?yàn)樽咏M件的未加載或錯(cuò)誤而導(dǎo)致父組件錯(cuò)誤),做到解耦和內(nèi)聚;
1、彈窗dialog和彈窗tips
因?yàn)閺棿癲ialog和彈窗提示tips可能同時(shí)存在,因此將其劃分為2個(gè)組件,方便管理;
2、未登錄頁(yè)面和登錄頁(yè)面
因?yàn)轫?yè)面存在登錄和未登錄狀態(tài),而為了加載速度考慮,當(dāng)未登錄時(shí),不加載已登錄頁(yè)面,因此需要?jiǎng)澐殖鰜?lái),并進(jìn)行異步加載處理;
3、未登錄頁(yè)面
未登錄頁(yè)面又分為三種情況:
1、初始頁(yè)面:毫無(wú)疑問(wèn)要直接加載
2、登錄彈窗:點(diǎn)擊登錄時(shí)加載(異步)
3、注冊(cè)彈窗:點(diǎn)擊注冊(cè)時(shí)加載(異步)
之所以分拆開(kāi),是因?yàn)楦鶕?jù)需求,已登錄用戶刷新頁(yè)面,可以直接進(jìn)入登錄后頁(yè)面,因此無(wú)需登錄和注冊(cè),這種處理可以減少流量消耗,提升加載頁(yè)面加載速度(特別是注冊(cè)彈窗需要加載的內(nèi)容還比較多);
4、已登錄頁(yè)面
已登錄頁(yè)面有較多頁(yè)面,采用默認(rèn)加載初始頁(yè),然后異步加載其他頁(yè)面(訪問(wèn)時(shí));
5、彈窗dialog
由于邏輯較少,代碼量不多,因此為了方便管理,統(tǒng)一將其合并在一個(gè)vue文件中,共同相同的打開(kāi)邏輯,根據(jù)傳遞的key決定打開(kāi)哪一個(gè)。
這樣在新增彈窗時(shí),無(wú)需再去寫(xiě)彈窗的打開(kāi)、關(guān)閉邏輯。
假如有較復(fù)雜的彈窗,可以以子組件的形式引入到當(dāng)前vue文件中,如此也方便管理;
6、國(guó)際化管理
和頁(yè)面高耦合,負(fù)責(zé)加載對(duì)應(yīng)的國(guó)際化資源,并進(jìn)行切換管理;
7、頁(yè)面組件
可能有子頁(yè)面和復(fù)用的組件,按照正常方式引用即可。
8、樣式文件
可以獨(dú)立寫(xiě)為.css文件,但因?yàn)槲业墓矘邮轿募容^少,因此我還是將其放在一個(gè).vue文件中,并在App.vue里來(lái)引用
8、頁(yè)面組件起名
通常以.vue為結(jié)尾,除了國(guó)際化LanguageManager.js因?yàn)楦唏詈隙?,因此?js結(jié)尾并是一個(gè)單獨(dú)的vue實(shí)例,表示他更像是一個(gè)功能模塊,而不是一個(gè)vue的頁(yè)面組件;
基礎(chǔ)頁(yè)面,如登錄和未登錄頁(yè)面,公共組件(并且是header和footer這種),以base-開(kāi)頭;
彈窗統(tǒng)一以box-為開(kāi)頭;
可復(fù)用的組件以extend-開(kāi)頭;
引入的外部組件以import-開(kāi)頭;
普通頁(yè)面組件以page-開(kāi)頭(這些頁(yè)面往往是一個(gè)獨(dú)立的頁(yè)面,并且掛靠在登錄或未登錄頁(yè)面下);
注冊(cè)彈窗因?yàn)檫壿嫳容^復(fù)雜,并且同類較多,因此以register-為開(kāi)頭;
通過(guò)以文件名來(lái)劃分,不同的頁(yè)面組件之間的區(qū)分可以說(shuō)是一目了然,同時(shí)也方便管理;
3、還沒(méi)有談到的內(nèi)容
想了想,目前還沒(méi)有談到的內(nèi)容有以下幾點(diǎn):
1、路由:
以路由為驅(qū)動(dòng)來(lái)寫(xiě)頁(yè)面,似乎是一種不錯(cuò)的選擇;
好處是只需要通過(guò)查看路由的配置文件router-config.js,就可以了解頁(yè)面組件的架構(gòu);
不過(guò)我自己關(guān)于vue-router的實(shí)踐經(jīng)驗(yàn)比較少,因此就略過(guò)不談了;
2、單元測(cè)試:
按道理說(shuō),單元測(cè)試是非常有必要的,因此在進(jìn)行架構(gòu)設(shè)計(jì)時(shí),必須考慮到這一點(diǎn);
不過(guò)作者我目前在的公司關(guān)于前端方面都不太正規(guī),實(shí)在沒(méi)這方面的經(jīng)驗(yàn),
又因?yàn)樽髡呶冶救宿D(zhuǎn)行做前端的時(shí)間也比較短,雖然周周9.12.7,但也沒(méi)來(lái)得及學(xué)習(xí)這方面的知識(shí)(沒(méi)有需求驅(qū)動(dòng),這方面動(dòng)力不足哈哈),
所以只能望有這方面經(jīng)驗(yàn)的同學(xué),愿意和我交流一下這方面的知識(shí),先謝謝啦。我的QQ是:20004604,郵箱是:20004604@qq.com
3、自動(dòng)化配置:
關(guān)于webpack的自動(dòng)化,是前端架構(gòu)的重要組成部分,因此配置也是蠻重要的;
不過(guò)一是這方面需要很多筆墨的,二是網(wǎng)上也有一些別人分享的文章(肯定比我的好),所以我這里就不細(xì)說(shuō)了;
4、一些自動(dòng)化工具:
依然尷尬,沒(méi)有大廠經(jīng)驗(yàn),路子比較野,所以大廠專有的一些工具是木有的,
而自己找的話又嫌麻煩,所以沒(méi)有優(yōu)化到極致,只做到了基本滿足目前項(xiàng)目需求的程度,見(jiàn)諒見(jiàn)諒;
等以后有這方面經(jīng)驗(yàn)的話我再寫(xiě)博客來(lái)分享吧~
---------------------
在線
客服
服務(wù)時(shí)間:周一至周日 08:30-18:00
選擇下列產(chǎn)品馬上在線溝通:
客服
熱線
7*24小時(shí)客服服務(wù)熱線
關(guān)注
微信
關(guān)注官方微信