前端程式碼規範

語言: CN / TW / HK

最近組內經常進行CodeReview,於是參考一些大廠規範以及一些開源的優秀原始碼,整理了一些前端程式碼規範,幫助我們後續可以寫出更好維護的程式碼。有什麼不對的地方,歡迎大家指出,一起學習進步!

先來看下下面這張思維導圖:

前端程式碼開發規範思維導圖 (1).jpg

下面會就幾個方面展開來說。

命名規範

駝峰式命名法介紹

  • Pascal Case大駝峰式命名法:首字母大寫。eg:PersonInfo
  • Camel Case小駝峰式命名法:首字母小寫。eg:PersonInfo

檔案命名

  • 所有檔名統一使用小寫,首頁命名為index.xxx,檔名禁止特殊字元比如空格、$等。統一使用英文單詞或拼音縮寫,必須小寫。( 為了醒目,某些說明檔案的檔名,可以使用大寫字母,比如README、LICENSE。 )
  • 檔名包含多個單詞時,單詞之間建議使用半形的連詞線 ( - ) 分隔。
  • 檔案目錄結構巢狀層級不要過深

變數命名

命名方式 : 小駝峰式命名方法命名規範 : 型別+物件描述的方式,如果沒有明確的型別,就可以使字首為名詞

| 表頭 | 表頭 | | -------- | -- | | array | a | | boolean | b | | function | fn | | int | i | | object | o | | regular | r | | string | s |

函式命名

命名方式 : 小駝峰方式 ( 建構函式使用大駝峰命名法 ) 命名規則 : 字首為動詞

| 動詞 | 含義 | 返回值 | | --- | ------------------ | ----------------------------- | | can | 判斷是否可執行某個動作 ( 許可權 ) | 函式返回一個布林值。true:可執行;false:不可執行 | | has | 判斷是否含有某個值 | 函式返回一個布林值。true:可執行;false:不可執行 | | is | 判斷是否為某個值 | 函式返回一個布林值。true:可執行;false:不可執行 | | get | 獲取某個值 | 函式返回一個非布林值 | | set | 設定某個值 | 無返回值、返回是否設定成功或者返回鏈式物件 |

例子:

// 是否可跳舞 function canDance(){ return true; } ​ // 獲取工作 function getWork{ return this.work }

常量命名

命名方法 : 全部大寫命名規範 : 使用大寫字母和下劃線來組合命名,下劃線用以分割單詞。

例子:

const PATH = "xxxx"

類命名

  • 公共屬性和方法 : 同變數命名方式
  • 私有屬性和方法 : 字首為下劃線(_或#)後面跟公共屬性和方法一樣的命名方式

註釋規範

單行註釋和多行註釋的空格儲存程式碼時eslint會幫我們處理,不用手動加空格。

單行註釋 ( // )

  • 單獨一行://(雙斜線)與註釋文字之間保留一個空格
  • 在程式碼後面添加註釋://(雙斜線)與程式碼之間保留一個空格,並且//(雙斜線)與註釋文字之間保留一個空格。
  • 註釋程式碼://(雙斜線)與程式碼之間保留一個空格

多行註釋 ( / 註釋說明 / )

  • 若開始(/*和結束(*/)都在一行,推薦採用單行註釋
  • 若至少三行註釋時,第一行為/*,最後行為*/,其他行以*開始,並且註釋文字與*保留一個空格。

函式(方法)註釋

函式(方法)註釋也是多行註釋的一種,但是包含了特殊的註釋要求

/** * 函式說明 * @關鍵字 */

常用註釋關鍵字

| 註釋名 | 語法 | 含義 | 示例 | | -------- | ----------------------- | --------- | ------------------------- | | @param | @param 引數名 {引數型別} 描述資訊 | 描述引數的資訊 | @param name {String} 傳入名稱 | | @return | @param 引數名 {引數型別} 描述資訊 | 描述返回值的資訊 | @param name {String} 傳入名稱 | | @author | @author 作者資訊 [附屬資訊:如郵箱] | 描述返回值的資訊 | @author 李四 2022/12/16 | | @version | @version XX.XX.XX | 描述此函式的版本號 | @version 1.1.1 | | @example | @example 示例程式碼 | 描述此函式的版本號 | |

檔案目錄結構

同功能放在同一個檔案目錄下,目錄結構不要巢狀過深,檔名語義化一些,方便後續維護。

  • 資料夾名稱全部採用小寫+"-" 來隔開;
  • 避免多層巢狀,單個專案中的目錄巢狀控制在最多三到四個層級內;

例子:

- src 開發目錄     - pages 檢視         - module-a 模組A           - components 私有元件             - ComA.vue             - ComB.vue           - index.vue         - module-b 模組B     - components 公共元件       - index.vue 匯出所有元件       - header         - index.vue     - utils 這裡是以utils為字尾,JS工具庫       - index.js       - a.utils.js       - b.utils.js     - hooks 這裡是以hooks為字尾       - index.js       - a.hooks.js       - b.hooks.js     - service api請求,這裡是以api為字尾       - a.api.js 按照後端微服務進行劃分       - b.api.js     - constans 常量

通過對工具函式、hooks、api等加上字尾,更加容易區分引入的檔案。

程式碼規範

JS

JS/TS主流的大致有這幾種:

可以參考star最多的進行配置,幾乎覆蓋了JavaScript的每一項特性。

下面會就程式碼層面作出闡述。

| 名稱 | 說明 | | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 命名規範 | 1. 普通命名採用小駝峰式命名
2. 命名是複數的時候需要加s
3. 命名需要符合語義化,儘量減少縮寫的情況發生,做到見名知意,如果函式命名,可以採用加上動詞字首 | | 變數規範 | 1. ​​變數定義​儘量使用const、let
2. ​變數兜底 | | 字串 | 1. ​統一使用單引號而不是雙引號,配置安裝eslint後,儲存會自動格式化處理
2. ​用字串模板而不是'+'來拼接字串
3. ​不要使用不必要的轉義字元
4. ​不要在字串中用eval(),漏洞太多 | | 陣列 | 1. 用擴充套件運算子(...)做陣列淺拷貝
2. ​使用陣列解構 | | 物件 | 1. ​ES6 使用屬性值縮寫,​將屬性的縮寫放在物件宣告的開頭
2. ​物件淺拷貝時,更推薦使用擴充套件運算子 ...使用物件解構 | | 函式 | 1. ​函式引數使用預設值替代使用條件語句進行賦值
2. 函式引數越少越好,如果引數超過兩個,要使用 ES6 的解構語法,不用考慮引數的順序。把預設引數賦值放在最後
3. ​儘量使用箭頭函式
4. ​用命名函式表示式而不是函式宣告,​函式宣告作用域會提升,降低了程式碼可讀性和可維護性
5. ​不要改引數,不要對引數重新賦值
6. ​功能函式使用純函式,輸入一致,輸出結果永遠唯一
7. ​優先使用函數語言程式設計 | | for迴圈 | 使用for迴圈過程中,陣列的長度,使用一個變數來接收, ​有利於程式碼執行效率得到提高,而不是每走一次迴圈,都得重新計算陣列長度|

Vue

遵循vue.js官方風格指南,http://vuejs.bootcss.com/style-guide/

元件

| 名稱 | 說明 | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 元件命名 | ​1. 元件名為多個單詞,命名為元件用途,完整單詞的元件名(傾向於完整單詞而不是縮寫)
​2. 檔名應該要麼始終是單詞大寫開頭 (PascalCase),要麼始終是橫線連線 (kebab-case),例如:todo-item或TodoItem
3. ​基礎元件名, 應用特定樣式和約定的基礎元件 (也就是展示類的、無邏輯的或無狀態的元件) 應該全部以一個特定的字首開頭,比如Base、App或V
4. ​單例元件名,只應該擁有單個活躍例項的元件應該以The字首命名,以示其唯一性
5. ​緊密耦合的元件名, ​和父元件緊密耦合的子元件應該以父元件名作為字首命名
6. 元件名中的單詞順序, 元件名應該以高級別的 (通常是一般化描述的) 單詞開頭,以描述性的修飾詞結尾
7.模板中的元件名大小寫,​對於絕大多數專案來說,在單檔案元件和字串模板中元件名應該總是PascalCase的——但是在DOM模板中總是kebab-case的
8. ​JS/JSX中的元件名大小寫,JS/JSX 中的元件名應該始終是 PascalCase 的,儘管在較為簡單的應用中只使用Vue.component進行全域性元件註冊時,可以使用kebab-case字串 | | 元件資料 | ​元件的data必須是一個函式 | | props定義 | ​​1. prop定義應該儘量詳細 ​
2. prop名大小寫,​在宣告prop的時候,其命名應該始終使用camelCase,而在模板和JSX中應該始終使用kebab-case。 | | v-for使用 | 1. 為v-for設定鍵值,儘量避免使用index作為key ​
2. 避免v-if和v-for用在一起 | | 樣式 | ​​為元件樣式設定作用域,使用scoped屬性,使用BEM約定 | | 私有property名 | 1. Vue使用字首來定義其自身的私有 property
2. 推薦使用$
,作為一個使用者定義的私有property的約定,以確保不會和Vue自身相沖突 | | 自閉合元件 | 在單檔案元件、字串模板和JSX中沒有內容的元件應該是自閉合的——但在DOM模板裡永遠不要這樣做 | | attribute | 1. 多個attribute的元素應該分多行撰寫,每個attribute一行 ​
2. 帶引號的attribute值 | | 模板中簡單的表示式 | 元件模板應該只包含簡單的表示式,複雜的表示式則應該重構為計算屬性或方法 | | 簡單的計算屬性 | ​應該把複雜計算屬性分割為儘可能多的更簡單的property,計算屬性不能產生副作用 | | 指令 | 指令縮寫 (用:表示 v-bind:、用@表示 v-on: 和用#表示 v-slot:) | | 元件通訊 | 應該優先通過prop和事件進行父子元件之間的通訊,而不是this.$parent或變更prop | | 事件、定時器 | 清除定時器或者事件監聽 | | 程式碼檔案 | 開發過程中單個檔案不允許超過600行,特別複雜的功能,檔案不允許超過1000行 |

模板中的元件名大小寫

PascalCase相比kebab-case有一些優勢:

  • 編輯器可以在模板裡自動補全元件名,因為PascalCase同樣適用於JavaScript
  • 視覺上比 更能夠和單個單詞的HTML元素區別開來,因為前者的不同之處有兩個大寫字母,後者只有一個橫線
  • 如果你在模板中使用任何非Vue的自定義元素,比如一個Web Component,PascalCase確保了你的Vue元件在視覺上仍然是易識別的

由於HTML是大小寫不敏感的,在DOM模板中必須仍使用kebab-case。

例子:

```

```

Prop名大小寫

我們單純的遵循每個語言的約定。在JavaScript中更自然的是camelCase。而在HTML中則是kebab-case。

檔案目錄

| 名稱 | 說明 | | ---- | --------------------------------------- | | 資源 | 資源統一放置在 assets 資料夾下,資源以資料夾組織,資料夾名稱即模組名稱 | | 公共方法 | 放置在utils內部 | | 樣式 | BEM命名規範 |

CSS

CSS檢查程式碼規範

使用stylelint外掛,規範則推薦使用stylelint-config-standard

下面簡單說下stylelint-config-standard使用

1. 安裝 yarn add -D stylelint stylelint-config-standard ​ 2. 在專案的根目錄中建立一個配置檔案.stylelintrc.json,內容如下: { "extends": "stylelint-config-standard" } ​ 3. 解決與prettier配置的衝突: ​ yarn add -D stylelint-config-prettier ​ 4. 將下面配置複製到.stylelintrc.json中: { "extends": ["stylelint-config-standard", "stylelint-config-prettier"] } ​ 5. 在 git commit 階段進行檢測: "lint-staged": {   "**/*": "prettier --write --ignore-unknown", // 格式化   "src/**.{js,jsx,ts,tsx}": "eslint --ext .js,.jsx,.ts,.tsx", // 對js檔案檢測   "**/*.{less,css}": "stylelint --fix" // 對css檔案進行檢測 },

BEM命名原則

  • block:模組,名字單詞間用-連線
  • element:元素,模組的子元素,以__與block連線
  • modifier:修飾,模組的變體,定義特殊模組,以--與block連線

有效使用css選擇器

有效使用css選擇器,需遵循以下原則:

  • 保持簡單,不要使用巢狀過多過於複雜的選擇器,選擇器巢狀應少於3級;
  • 萬用字元和屬性選擇器效率最低,需要匹配的元素最多,儘量避免使用;
  • 避免使用CSS表示式;
  • 慎重選擇高消耗的樣式(高消耗屬性在繪製前需要瀏覽器進行大量計算),避免重繪重排;
  • ​css選擇器中避免使用標籤名;
  • ​儘量使用縮寫屬性;
  • ​使用子選擇器;
  • 0後面不帶單位;
  • id和class,命名​名稱語義化,不要過於簡單,防止模組之間樣式互相影響;​​合理的使用id,​​一般情況下id不應該被用於樣式,並且id的權重很高,所以不使用id解決樣式的問題,而是使用class;

CodeReview常見程式碼問題彙總

| 類別 | 描述 | 說明 | | ---- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 檔案 | 命名 | 1. 元件命名規範,儘量不要和現有元件或遠端元件重合,比如頁面裡使用元件時直接使用Table
2. 檔案命名儘量語義化,如果沒有定製化的,檔案命名不要太定製化,比如檔案命名直接是fifth-floor
3. 檔案中變數/方法命名語義化,方法名格式統一 | | UI規範 | 刪除按鈕顏色 | 刪除類的操作按鈕顏色使用紅色 | | vue | 程式碼問題 | 1. 元件資料共享: 巢狀呼叫的元件宣告一個方法,直接返回當前模組的資料,不要層層巢狀,通過$refs獲取,比如:元件裡定義一個getValues方法獲取資料
2. 生命週期鉤子:vue生命週期如果沒有依賴關係的話,儘量不要用async/await,可以把請求封裝成一個方法,生命週期中直接呼叫方法;如果有依賴關係的話就用,看場景
3. v-for中key值繫結,儘量不要繫結index,使用id,如果沒有id且不涉及新增刪除操作時,可以繫結index
4. 使用vue/composition-api時,相同變數以及方法考慮是否放在一起,方便看,看場景及個人習慣
5. 引入第三方工具包時儘量使用小的包,比如moment包換成dayjs
6. 使用動態路由(id)
7. 不建議this傳遞,問題排查容易出問題,即在其他頁面修改this裡的變數 | | JS | 程式碼問題 | 1. map/forEach: 注意區分兩者使用場景,不要隨便使用
2. 常量:頁面裡使用多次的字元統一使用常量對映,不要直接在頁面中使用字元
3. 程式碼簡潔性:避免使用多次迴圈列表,如果列表資料很多會有效能問題,比如filter和map巢狀使用
4. 三元表示式: 使用時根據場景可以換成或
5. 否定前置
6. 空資料:介面返回資料為空相容判斷
7. 陣列遍歷for迴圈修改為for/of
8. dayjs/moment可以轉換一切時間形式為format,不止是時間戳還有標準時間
9. 空值合併運算子(??)使用: 當左側的運算元為 null 或者 undefined 時,返回其右側運算元,否則返回左側運算元
10. dayjs().format('YYYY-MM-DD HH:mm:ss')無引數時預設取的當前時刻; 有引數時可以考慮把引數提取出來
11. toString()可以寫為join(',')
12. 資料為空相容:用或、?.表示
13. if switch可以考慮轉換為json map形式
14. a或b或c改為 [].includes()
15. try/catch捕獲錯誤異常console.dir(error); | | ES6 | 程式碼問題 | 1. map迴圈中可以使用解構的話換成解構,避免多層巢狀
2. 解構:能解構儘量解構,邊界值相容處理 | | CSS | 程式碼問題 | 1. 類名:類名注意不要太簡單,直接取name/title/desc之類的,容易和其他人寫的類名衝突,比如別人寫了同名的類名沒有設定scoped或者全域性類名,會影響自己的樣式 |

map/forEach說明

比如:

```

demo1

arr.map(({value = {}, ...item})=>{    return {          ...item,          ...value    }}) ​

demo2

const type = this.alarmType?.map(item => ({alarmTypeId: item})) || []; ​

demo3 push行為修改為map

this.evidence.printScreen = data.printScreen?.map( ({ id, title, value }) => ({   id,   title,   value,   url: ${imgUrlPre}${id}, }) ); ```

dayjs.format(str)說明

dayjs.format(str) ,str抽離成下面形式 const DATE_FORMAT_TYPE = { date: 'YYYY-MM-DD', time: 'HH:mm:ss', dateTime: 'YYYY-MM-DD HH:mm:ss', };

相關資料: - BEM規範:http://getbem.com/naming/