前端架構(gòu)存在的目的,
1、提高代碼的可讀性。
一個好的架構(gòu),代碼的可讀性一定是強的。
簡單來說,假如有一個新人加入團隊,那么他接手這個項目,一定是容易上手的,能簡單輕松的了解整個前端部分的相互關(guān)系,從而找到自己需要重點關(guān)注的點。而不是需要花很多時間去熟悉這個項目的很多細節(jié),才能開始上手做東西。
就文件來說,可以從文件名上,分清哪些是頁面、哪些是邏輯、哪些是樣式、哪些是可以復用的組件、哪些是圖標組、又有哪些是移動端或是PC端專享的樣式/邏輯等。
就代碼來說,包括統(tǒng)一的命名風格,封裝在同一個文件里的代碼的相關(guān)性足夠強等。
2、提高代碼的可維護性。
一個好的架構(gòu),一定是易于維護的,例如在新增需求、更改需求、修正bug,都不會造成意料之外的變化,比如說修改了一個頁面組件的內(nèi)容,卻導致另外一個頁面組件發(fā)生變化(這也太坑了)。
因此,要低耦合,高內(nèi)聚,以及輸入和輸出是可預期的。
3、提高代碼的可擴展性。
一個好的架構(gòu),一定擴展性要強,不能寫死。
需求變更太正常了,新增需求也太正常了。因此好的架構(gòu),必須要考慮到這些情況的發(fā)生,因為他們是一定會發(fā)生的。所以,一定要避免把代碼寫死。
比如頁面組件A里需要有一個日歷組件,而這個日歷組件引用的是別人的(比如從github上找的),那么盡量不要直接在頁面組件A里面直接引用這個日歷組件,而是將寫一個日歷組件B,在這個日歷組件B里封裝你引用的日歷組件C,然后通過這個日歷組件B來進行操作。
原因很簡單,假如某天產(chǎn)品經(jīng)理說,這個日歷組件太丑了,我們換一個吧。如果你直接在頁面組件A里內(nèi)嵌這個引用的日歷組件C,你很可能就要改很多代碼(因為不同日歷組件的使用方法和暴露的接口可能不同)。假如你還在其他多個地方引用了這個日歷方都要改。
而若是將引用的日歷組件C封裝到自己寫的日歷組件B之中,那么你只需要改日歷組件B里的相應代碼即可,而因為日歷組件B暴露的接口是不變的,那么自然不用修改頁面中的代碼了。
4、便于協(xié)同。
包括前端和后端的協(xié)同,前端和前端之間的協(xié)同。
具體來說,前后端的協(xié)同通常是以ajax為交互,那么應至少有一個用于專門封裝了所有ajax請求的文件,所有ajax請求都封裝在這里。在開發(fā)時,這里封裝的方法應該可以模擬發(fā)送和接收約定好的交互內(nèi)容,方便開發(fā)聯(lián)調(diào)。
而前端和前端的協(xié)同,主要體現(xiàn)在同時在更改代碼時,不會影響對方代碼的正常運行。因此要求封裝、解耦以及低干擾度是必須的。
5、自動化
自動打包,壓縮,混淆,如果有必要,再加上自動單元測試。
前端架構(gòu)師們認為有多個關(guān)鍵的決策需要在項目啟動之初就制定下來,如果等到開發(fā)階段的后期再考慮,不是已經(jīng)用不上,而是一開始錯誤的決定已經(jīng)造成了無法挽回的損失。一旦做出這些決策,我們的任務就是去輔助視覺設(shè)計、平臺開發(fā)、底層結(jié)構(gòu),使之能最大程度滿足需求,要做到以下幾點;
模塊化標記
我們都在追求的理想的狀態(tài)是,網(wǎng)站每一行HTML都由程序自動生成,而作為前端開發(fā)人員,我們只需要管理這個用來產(chǎn)生標記的模板和流程,遺憾的是,現(xiàn)實通常并非如此。即使在最好的情況下,也存在用戶生成的內(nèi)容,而這些內(nèi)容幾乎都無法自動添加CSS類名來標記。無論CMS系統(tǒng)(可以理解為后臺)自動生成HTML的能力如何,讓CMS決定類似表單和導航欄這樣的標記,有時候會更簡單。
模塊化標記和手寫的靜態(tài)標記的區(qū)別在于,程序化地執(zhí)行完之后,我們還可以通過一套類名系統(tǒng)給標記動態(tài)添加CSS類名。而且不再通過元素標簽和層級關(guān)系來決定視覺外觀。讓我們看看如何用BEM原則(一個CSS規(guī)則,下面會說到)模塊化地實現(xiàn)一個簡單的導航:
<nav class="nav">
<ul class="nav__container">
<li class="nav__item">
<a href="/products" class="nav__link">
<ul class="nav__container--secondary">
<li class="nav__item--secondary">
<a href="/socks" class="nav__link--secondary">
要使用這種模塊化方法,我們首先需要改變構(gòu)建頁面的方法和思路。作為前端開發(fā)人員,我們的工作就是把視覺語言拆解成最小單元,拆解之后,我們可以創(chuàng)建規(guī)則,對這些最小單元進行重組。
上面提到的BEM(Block Element Modifier,塊元素修飾符)是一個CSS類名命名規(guī)則,它建議每個元素都添加帶有如下內(nèi)容的CSS類名:
塊名。所屬組件的名稱。
元素。元素在塊里面的名稱
修飾符。任何與塊或元素相關(guān)聯(lián)的修飾符
過多的CSS依賴
如果我們想渲染我們樣式中的某一塊內(nèi)容,首先需要加載以下內(nèi)容
Boostrap CSS: 114KB(未壓縮)。其實網(wǎng)站本身沒怎么調(diào)用Boostrap庫的代碼,但我們編寫所有CSS的前提條件都是Boostrap已經(jīng)重置了基本樣式。
核心的網(wǎng)站CSS: 500KB。雖然一般來說,每塊內(nèi)容都有一個單獨的關(guān)聯(lián)文件。但這個文件不是這塊內(nèi)容的單一樣式來源。樣式不僅來源位置多樣,并且常?;谖恢煤晚撁娴念惐桓采w重寫。
自上而下的樣式命名方法意味著,每次修改我們都要寫一個更長的、更具體的選擇器,同時,因為標記順序極為嚴格,每塊內(nèi)容都很難重排或者替換。當然我們可以抽出一個單獨文件,并把它需要的所有樣式合并到單獨的一個文件里,但是這么做基本意味著完全重做這個組件里的CSS文件,每塊內(nèi)容都很難重排或者替換。
因此我們指定了一些規(guī)則,以下是我們指定這些規(guī)則時需遵循的規(guī)范:
只包含不可變的規(guī)則,而不是籠統(tǒng)的說明。
總是把規(guī)則提煉成最簡單的表達。
總是首先說明規(guī)則時什么,再說明“如果不這樣,那么會怎么樣”
每個規(guī)則必須包含以下詞的一個——總是、永遠不要、只有、每一個、不要、要
最后指定的設(shè)計系統(tǒng)的規(guī)則如下
永遠不要給布局的子內(nèi)容強加內(nèi)邊距和元素樣式。布局只關(guān)注垂直對齊、水平對齊和文字間距。
主題和別的數(shù)據(jù)屬性值永遠不要強制改變外觀,他們必須保持布局、組件和元素可以應用于其上。
組件總是貼著它的父容器的四個邊,元素都沒有上外邊距和左外邊距、所有的最后節(jié)點(最右邊和最下邊的節(jié)點)的外邊距都會被清楚。
組件本身永遠不要添加背景、寬度、浮動、內(nèi)邊距和外邊距的樣式,組件樣式是組件內(nèi)元素的樣式。
每個元素都有且只有一個唯一的且作用于只在組件內(nèi)的CSS類名,所有的樣式都是直接應用到這個選擇器上, 并且只有上下文和主題能修改元素的樣式。
JavaScript
為js創(chuàng)建規(guī)范是非常有必要的,可以參考以下建議:
保持代碼整潔。JS Hint是這寫工具中一個很好的例子,這里有幾條可以通過JS Hint檢查的規(guī)則:
強制使用===和!==代替==和!=
限制代碼塊嵌套深度
限制函數(shù)的參數(shù)數(shù)量
如果函數(shù)重復定義,發(fā)出警告
如果變量創(chuàng)建后未被使用,發(fā)出警告
創(chuàng)建可復用的函數(shù)。比如我們常用的addClass操作,如果需要多次創(chuàng)建新的.green-alert類名,只需要修改定義好的add_background方法:
$.fn.add_background = function(color){
this.css('background-color', color);
return this;
}
測試核心;
在開始為應用程序規(guī)劃測試時,請記住以下幾條建議:
測試用例應該在建站的同時或之前開始編寫
測試代碼是真實的代碼,應該一起或立即提交到系統(tǒng)代碼庫中。
必須在所有的測試用例都通過之后,才能把代碼合并到主干上。
在主干上運行測試工具,結(jié)果都應該為通過。
單元測試時將應用程序分解為盡可能小的函數(shù),并創(chuàng)建可重復的、自動化的測試用例的過程?!耙淮沃蛔鲆患?,并把它做好”是構(gòu)建基于單元測試的應用程序的原則(函數(shù)式),我們在寫函數(shù)時經(jīng)常想同時實現(xiàn)很多功能,結(jié)果最后不僅降低了了效率,還增加了測試的難度。
單元測試的基本思路是調(diào)用要測試的函數(shù),傳遞一些預先設(shè)置好的參數(shù),并描述結(jié)果是什么。
function calculateShipping(distance){
switch(distance){
case(distance < 25):
shipping = 4;
break;
case(distance < 100):
shipping = 5;
break;
case(distance < 1000):
shipping = 6;
break;
return shipping;
}
}
QUnit.test('Calculate Shipping', function(assert){
文章內(nèi)容來源于網(wǎng)絡,侵刪