前端代碼規範

語言: 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/