Code 前止步!前端需要注意的程式碼撰寫規範
身為前端,會有非常多機會需要與其他前端合作開發。在合作路上,相信不少前端都有以下經驗 😧
- 為什麼要針對 HTML Tag 處理樣式或邏輯?
- 為什麼 HTML 結構要寫得這麼深?
- 為什麼不寫成組件共用?
- 為什麼區塊命名這麼隨便?
- 為什麼沒用到區塊不刪掉也不備註原因?
上述幾點,主要是因為個人撰寫的習慣不同而造成的問題。專案通常只會規範基礎項目,譬如開發工具版本資訊、專案的檔案說明、layout 大架構設定等。看過上述那些痛點,相信大家也明白,前端的撰寫風格是有必要進行規範的。
在海棠,每個專案都有規範要求,若是專案的開立人則能夠依照狀況額外再加入規範;若是協作者,也能提出相關建議,與主管及前端們討論是否有必要加到固定的規範文件中。這樣做的目的很簡單,就是要避免痛點再次發生!
於是乎就想來跟大家分享,在 CSS、JS及全站這三個方面,前端需要注意的撰寫方式!
針對 CSS 的寫法
避免 CSS 層級太深、避免在 HTML Tag 上處理樣式
瀏覽器是從右至左開始讀取 CSS 的規則,舉個例子:
這是一個區塊的架構,假如要處理樣式是 body 裡面的 p 。依據從右至左的規則,瀏覽器得先找到頁面所有 P,再一層一層往上找符合條件的節點。依照下述寫法,就是爸爸不對、爺爺不對、阿祖不對,得往上直到找對為止。
雖然也能處理樣式,不過這樣寫的問題是 :
- 要寫樣式的是 .info,一頁可能就1–2個,為何要花效能去抓全站的 P Tag?
- 在 P Tag 上寫樣式,那 span 或其他標籤想用怎麼辦?
- 層級深,後續對 .info 的調整,層級也得一樣深,且只要更動 HTML 結構,樣式就會大亂
- 層級深,會讓壓縮後的樣式檔案增肥
如果名稱能代表區塊的獨特性,階層就不需要寫太深,甚至名稱沒重複且夠獨特,直接寫 .box_body_info 也沒問題。
.wrap .box .info {} // 少層,針對 .info
.box_body_info {} // .info 改為 .box_body_info 更簡單
這樣寫的好處是 :
- 只針對特定的 Class 處理樣式,提高瀏覽器效率
- .box_body_info 可以無限套用在需要的標籤上
- 層級短,不怕結構改變產生問題
- 層級短,樣式檔瘦一圈
避免使用無意義的 CSS 名稱
CSS 的命名與 HTML 區塊架構息息相關。前端很常用區塊的類型來命名,譬如產品卡片 product_card 或購物車 shopping_cart 。明確的命名,能夠讓前端第一眼分辨這是什麼的區塊,並快速查到相關設定。
// 用清晰明瞭的名稱,讓前端快速進入狀況。
<div class="products">
<div class="product_card">...</div>
</div>
無意義的命名,在閱讀上就是一種困擾,只適合練習用拉 🙏
// 避免使用無意義的名稱
<div class="aaaaa">
<div class="bbbbb">...</div>
</div> // Style
.aaaaa .bbbbb {} // Script
document.queryselector('.aaaaa .bbbbb');
有跡可循的 CSS 命名方式
每個前端有自己習慣的命名方式,光是產品卡片可能就有以下好幾種命名:
.product_cart {}
.product-cart {}
.product__cart {}
.productCart {}
只要能讓人快速辨識出區塊的作用,其實也不會有太大的影響。不過在撰寫的時候,固定一種命名方式會比較好。若想要整理出一套有跡可循的撰寫風格,可以參考以下幾個命名規範:
▶ OOCSS(Object Oriented CSS)
大家比較熟知的 CSS 框架 Bootstrap 是用了 OOCSS 的命名規範,透過組合的方式,讓樣式更容易維護。OOCSS 遵循以下兩個原則:
- Separate structure and skin:結構是構成元件的基本設定,如字型樣式、寬高;樣式則是顏色尺寸等設定。
好比說 Bootstrap 的 Button 組件,將樣式分類為狀態跟尺寸,提高了組件使用的彈性。
// 元件基本結構
.btn {} // 負責管理元件狀態(顏色)
.btn-primary {}
.btn-success {} // 負責管理元件尺寸
.btn-sm {}
.btn-lg {}
- Separate container and content:將可共用的樣式,拆成獨立Class,不必依賴於某些內容之下。
#shopping_cart
.steps {
margin-bottom: 3px;
width: 140px;
}// 拆解為單一組件
.steps {
margin: 3px;
width: 140px;
}
▶ BEM(Block,Element,Modifier)
是由區塊、元素與修飾狀態組成。特色是用兩個底線(_)接著元素、兩個破折號(-)接著狀態。
// 區塊__區塊底下元件--狀態
.header__navmenu--open{}
BEM 是非常簡單有力的命名方式,不過缺點是名稱很容易越寫越長。
若是專案沒有規定命名規則,可以參考上述那些規範,理出一套撰寫風格。譬如說:
- 用一個底線(_)來指定區塊內的元件
- 用 .is- 表示狀態,譬如已加入購物車
- 用 .vm- 表示同組件的另一種模式,譬如是橫的產品卡片
- 用 .js- 表示受邏輯控制
- 模仿 bootstrap ,撰寫樣式組件 btn-primary
<div class="products">
<div class="product_cart is-added vm-product js-products">
<a class="btn btn-primary" href="#" role="button">Link</a>
</div>
</div>
避免使用 !important
一般來說,前端是需要避免使用 !important 的,因為 !important 是一種提高樣式權重的做法,通常會在當頁面龐大、結構太深不易再調整的情況下,將樣式硬壓上去。
藉由 CSS 的權重順序,來說明 !important:
▶ 多層 > 低層
越多層缺點是,後續調整也得寫得一樣長。若是用 !important,一下就壓過去了呢!
▶ ID > Class > Tag
ID的權重很高,又具唯一性,建議都以 Class 處理樣式。唯一性像是 Header, Footer 等 layout 設定就可以考慮是否要用 ID。若是用 !important,一下就壓過去了呢!
▶ 行內樣式 inline-style > 引入樣式
越靠近元素的樣式,權重越高。若是用 !important,一下就壓過去了呢!
上述舉例可看出,!important 無視了樣式的權重規則,為了要修改被!important「硬壓」過的樣式,只能再用「硬壓」的方式,層層壓上去。這樣非常不利於後續的開發與維護。
針對 JS 寫法
將邏輯寫在閉包裡
當處理功能的檔案一多,就有可能重複賦值已有的 function 或變數,導致某些已經正常的功能出現 Bug。
閉包可以限制函式與變數的作用域僅在該範圍內,外層雖然取用不到閉包內的事件,但閉包能夠取到外層的邏輯。所以除了需要被共用的邏輯之外,一般給單元用的邏輯就可以寫在閉包裡。
"use strict"; (function (window, document) { ... })(window, document);
加上有無 DOM 時的判斷
通常一個單元的頁面,若是有共用邏輯,就會引入相同的 JS 檔案來做處理。很常發生的狀況是:A 頁有 Demo 區塊,而 B 頁沒有,為了避免錯誤發生,應該要先判斷該 Dom 是否存在。
// 如果有長度才執行
// javascript
var pdCart = document.querySelectorAll('.product_card'); if(pdCart.length) { ... }// jquery
if($('.product_card').length) { ... }
針對全站
善用組件化
組件化是個很重要的概念,前端必須思考如何避免重工,讓相同的區塊、樣式與邏輯能夠重複利用。如同前面所述,若是區塊結構簡潔乾淨、樣式命名獨特,那這個區塊的彈性與靈活度就會非常高。
舉例來說 Bootstrap 已經定義好許多 Components,只要依照文件給的範例結構就能直接拿來使用,譬如 Buttons 組件,按鍵的樣式 btn 可以套用在 a, button 或 input 上,再透過 btn-名稱 來調整狀態。
<a class="btn btn-primary" href="#" role="button">Link</a> <button class="btn btn-success" type="submit">Button</button> <input class="btn btn-light" type="button" value="Input">
不只是樣式,使用過 pug 的人,就會知道組件是非常棒的懶人聖品。設定全域 mixin,再依照頁面需求,引用需要的 mixin 就好了。
mixin article(title)
.article
.article-wrapper
h1 #{title}
p No content provided +article('Hello world')
有組件前,必須頁頁改;有組件後,改一次搞定全部,何樂而不為?
善用註解
- 有設定狀態的區塊
備註可以幫助其他前端在開發或編修區塊時,快速了解有哪些樣式與作法,避免遺漏了設定導致功能失靈、樣式跑版。譬如區塊的狀態是利用額外的 Class 來處理,就可以用備註的方式,列出處理狀態的 Class 們:
// is-executing 綠色 -> 正在執行, is-warn 紅色 -> 已過期
<div class="box is-warn">...</div>
- 無用的架構與樣式
依據開發與編修需求,前端會自行判斷是要刪除,還是隱藏這些區塊。我們可以在被註解的區塊上方標註暫時隱藏的原因,讓其他前端知道這區塊暫時不能刪,避免後續大家都忘了,變成「刪也不是又刪也不是」。
建立與遵守專案規範
海棠的前端 Vivian Wu 之前分享的Web DesignFront-End 製作網頁從0到有,可以了解到一位開案者在進案後,要做好哪些項目。其中,README.md 是專案最重要的文件,紀錄專案所有資訊,如啟動與開發的工具資訊、注意事項等。所以每個進入新專案的前端,都必須要先看過 README.md 文件。
▶ 如果你是開案者
在海棠,可以依照專案需求或開案者的需求,補充注意事項在 README.md 上,譬如上述那些撰寫注意項目:
- 使用哪種 CSS 的命名規則,或連接符號規定
- 哪些區塊必須寫成組件,路徑是哪裡
- 改用 rem 為單位
- 註解區塊請用備註說明時間、原因
- 文件格式請以 4 tabs 為主
撰寫規範越是統一,前端就能多快進入狀況。
▶ 如果你是協作者
協作者必須遵照已有的專案規則進行開發。從開立的相關文件以及 README.md 中,了解有哪些規範要遵守。若是文件中沒有規範撰寫風格,我們可以先觀察已經完成的頁面,順著前人前端撰寫的方式處理,維持專案的統一性。
需要注意的是:協作者不能在沒告知開案者的情況下,自行改動已經定義好的全站規則,譬如內容寬度、間距、字級與用色,避免影響已經完成的頁面,額外定義樣式是比較好的做法。
結論
專案如同一篇文章,前端的撰寫風格會影響整篇文章的起承轉合,如何將邏輯寫順,讓自己跟其他前端夥伴在閱讀時,可以快速進入狀況?如何讓結構清楚明瞭、富彈性又易擴增?這些都是前端在撰寫時需要思考的問題。
P.S.「三人行,必有我師焉。」每個專案都藏著很多寶物是需要自己去挖掘的,看看前端夥伴的 Code,同時檢視自己撰寫的項目,也是自我成長的一種好方法~