一份整理好了的規范文檔,node后端開發用到
"規范是個好東西..." - 魯迅
以下規范僅作為參考
1、代碼規范
命名
- 盡量保證命名更加語義化
文件命名采用下划線命名法
// good
service_center.js
// bad
serviceCenter.js
類 & 構造函數命名
- 類命名采用 Pascal命名法,大寫字母開頭,各個單詞首字母大寫
class Person {
constructor(name) {
this.name = name;
}
}
const person = new Person('小明');
方法名
- 方法命名采用 Camel命名法,小寫字母開頭,各個單詞首字母大寫
前綴應當是動詞
命名建議:可使用常見動詞約定等。
// good
function getName() {
...
return this.name;
}
function isExist() {
...
return true;
}
變量命名
- 采用 Camel命名法,小寫字母開頭,各個單詞首字母大寫
特有大寫縮寫詞匯保持大寫如:SQL, URL等
變量名字不宜過長,可適當采用縮減英文元音字母來縮短長度
前綴應當是名詞(方法名前綴為動詞,以此區分變量和方法)
// good
let maxLength = 10;
// bad
let setLength = 10;
常量命名
- 必須采用 全大寫命名,且單詞以_分割
const MAX_COUNT = 10;
const SQL = 'SELECT * from bas_table';
注釋規范
- 行內注釋:行內注釋以兩個斜線開始,以行尾結束。雙斜線與代碼以及注釋文字之間都都保留一個空格。
function addNumber() {
return 3 + 2; // 5
}
- 單行注釋:單獨一行,單行注釋以兩個斜線開始,以行尾結束。
function addNumber(a, b) {
return a + b;
}
// 調用求和方法
addNumber(2, 3);
- 方法注釋:函數(方法)注釋也是多行注釋的一種,但是包含了特殊的注釋要求。
/**
- 方法描述
- @param {type} 參數值 參數說明
- @param {type} 參數值 參數說明
/
示例:
/* - @param {number} a 參數a
- @param {Number} b 參數b
*/
funtion addNumber(a, b) {
return a + b;
}
格式規范
- JS格式規范要嚴格遵循 Eslint,要求在 husky 鈎子中增加Eslint規則校驗,校驗不通過,代碼提交失敗。
其他規范
- 變量聲明
- 對所有的變量優先使用const,可變的變量使用let,不允許使用var
// good
const a = 1;
const b = 2;
// good
let count = 1;
if (true) {
count += 1;
}
- Strings
- 字符串使用單引號 ‘’
生成字符串時,使用模板字符串代替字符串連接
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
- 對象
- 使用字面值創建對象
// good
const obj = {};
// bad
const obj = new Object();
對象方法的簡寫
// bad
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
對象屬性值的簡寫
const lukeSkywalker = 'Hello World';
// bad
const obj = {
lukeSkywalker: lukeSkywalker
};
// good
const obj = {
lukeSkywalker
};
- 數組
- 使用字面值創建數組
// bad
const arr = new Array();
// good
const arr = [];
- 使用拓展運算符 … 復制數組
// bad
const len = arr.length;
const arrCopy = [];
let i;
for (i = 0; i < len; i++) {
arrCopy[i] = arr[i];
}
// good
const arrCopy = [...arr];
- 解構
- 使用解構存取和使用多屬性對象
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
- 函數
- 盡可能使用箭頭函數
不允許在一個非函數代碼塊(if、while 等)中聲明一個函數
使用函數聲明代替函數表達式因為函數聲明是可命名的,所以他們在調用棧中更容易被識別。此外,函數聲明會把整個函數提升(hoisted),而函數表達式只會把函數的引用變量名提升。這條規則使得箭頭函數可以取代函數表達式。
// bad
const foo = function () {
};
// good
function foo() {
}
- 別保存 this 的引用。使用箭頭函數或 Function#bind。
// bad
function foo() {
const that = this;
return function() {
console.log(that);
};
}
// good
function foo() {
return () => {
console.log(this);
};
}
語法原則
- 統一使用Node.js支持的 ES6 及以上版本的新特性寫法
- 盡量使用常用常規的方法去實現一個功能,避免使用各種奇技淫巧
- 能用原生語法或原生函數實現的功能盡量不要借助 lodash 等額外的庫來實現
- 統一使用 Promise + async/await 取代 callback, 解決回調地獄
- require() 引入的模塊和庫按代碼長度排序,且庫和自定義模塊分開,庫引用在前,自定義模塊在后
- 對象定義統一用 {} 定義,而不是 new Object()
- 使用字面值創建數組,數組定義統一用 [],而不是 new Array()
- 向數組添加元素時使用 push 替代直接賦值
- 使用拓展運算符 ... 復制數組
- 使用 map、reduce、filter 時如果函數簡單應省略 return
- 給注釋增加 FIXME 或 TODO 的前綴,幫助其他開發者快速了解這是一個需要復查的問題,或是給需要實現的功能提供一個解決方式。
命名規范:
- 函數名/變量名:
方式: 小駝峰
good: testFunction/testVar
bad: test_function/test_var - 全局常量:
方式: 大寫下划線分隔
good: TEST_CONST
bad: test_const - 文件名:
方式: 小寫下划線分隔
good: test_file.js
bad: test-file.js - 類名:
方式: 小寫下划線分隔
good: TestClass
bad: testClass
2、api設計
路由規范(URI)
- URI必須全部使用小寫
good: /api/v1/uic/check/login
bad: /api/v1/uic/checkLogin
- 如需連接多個單詞則必須使用 下划線
good:/api/v1/dubhe/task_run_history
bad: /api/v1/dubhe/task-run-history
- 版本號格式(必須是 v + 整數)
good:/api/v2/dubhe/tasks
bad: /api/v1.1/dubhe/tasks
HTTP Method
- 能抽象成資源的CURD則使用標准的HTTP Method來區分,URI中盡量不使用動詞(特殊情況除外[1])
- GET 讀取資源(必須保證接口的冪等性)
- POST 新增資源
- PUT 更新資源
- DELETE 刪除資源
- 資源的描述盡量采用復數形式
good: GET /api/v1/dubhe/tasks
bad: GET /api/v1/dubhe/task_list
- 對於不方便直接抽象為資源的操作,例如 啟動一個任務 可采用如下動詞的方式
POST /api/v1/dubhe/start/task/:taskId
- 參數傳遞主要使用如下三種方式(文件上傳除外)
- 路由參數(Route Param)
- URL查詢參數(Query String)
- JSON參數(Request Body)
- 單個資源的操作推薦使用動態路由參數(Route Param)
good:GET /api/v1/uic/users/:userId
bad: GET /api/v1/uic/users?userId=1234
- 參數命名方式統一采用駝峰式
good:
POST /api/v1/uic/users
body:
{
"userId": 1,
"userName": "用戶名",
"tenantId": 1,
"tenantName": "租戶名"
}
◦
bad:
POST /api/v1/uic/users
body:
{
"user_id": 1,
"user_name": "用戶名",
"tenant_id": 1,
"tenant_name": "租戶名"
}
接口返回值格式
-
默認統一格式為 application/json (文件下載除外)
-
返回體json數據中包含如下4個字段:
success : 描述接口返回成功還是失敗,取值 true or false
code: 接口返回的錯誤碼,錯誤碼的規范由各個業務系統自行約束
推薦使用英文大寫枚舉值,如: ERROR_UNAUTH ERROR_INVALID_PARAM ERROR_NO_PERMISSION
message: 接口返回錯誤時的描述信息,如: param transform failed, userId should be a number.
content: 接口返回成功時的數據內容, json格式
示例:
成功:
{
"success": true,
"code": "",
"message": "",
"content": {
"id": 1,
"name": 2,
"email": "12345@sina.cn"
}
}
注:content的內容及格式由接口實現者給出
失敗:
{
"success": false,
"code": "ERROR_NO_PERMISSION",
"message": "you have no permission",
"content": ""
}
◦
- 查詢接口分頁返回場景
- 查詢參數(querystring):?currentPage=1&pageSize=10
{
"success": true,
"code": "",
"message": "",
"content": {
"count": 20, // 數據總條數
"currentPage": 1, // 當前頁碼
"pageSize": 10, // 每頁的數據條數
"data":[
]
}
}
注:data中的內容及格式由接口實現者給出
標桿API設計樣例
- 名稱:查詢功能點列表
- 方法:GET
- 路徑:/api/v1/uic/functions
- 成功返回接口示例:
{
"success": true,
"code": 0,
"message": null,
"content": [
{
"functionId": 6530,
"functionName": "登錄權限",
"functionCode": "dsp_page",
"description": null,
"type": "1",
"productId": 102,
"groupId": null,
"groupName": null,
"enabled": 1,
"invalid": "N",
"createTime": "2018-03-21T03:13:06.000Z",
"modifyTime": "2018-03-21T03:13:06.000Z"
},
{
"functionId": 6528,
"functionName": "用戶頁面-管理",
"functionCode": "user_page",
"description": null,
"type": "1",
"productId": 85,
"groupId": null,
"groupName": null,
"enabled": 1,
"invalid": "N",
"createTime": "2018-03-13T13:35:11.000Z",
"modifyTime": "2018-03-13T13:35:11.000Z"
}
]
}
3、版本號規范
此規范約束的是package.json中的version以及git倉庫中打tag的規范。
- 版本號格式:
標准的版本號必須采用 X.Y.Z 的格式,其中 X、Y、Z 為非負的整數,且禁止在數字前方加前導零。X 是主版本號、Y 是次版本號、 Z 為修訂號。每個元素必須以數值來遞增。例如:1.9.0 -> 1.10.0 -> 1.11.0
- 版本號修改規則:
- 主版本號:當你做了不兼容的API修改的情況需要增加
- 次版本號:當你做了向下兼容的功能性新增需要增加
- 修訂號 :當你做了向下兼容的問題修正需要增加
基本規范 - 主版本號為零(0.y.z)的軟件處於開發初始階段,一切都可能隨時被改變。這樣的公共 API 不應該被視為穩定版。
- 1.0.0 的版本號用於標識穩定 API 的發布。這一版本之后,所有的版本號更新都基於公共 API 的修改內容
- 帶版本號的軟件發行后,禁止改變該版本軟件的內容。任何修改都必須以新版本發行。
修訂號 Z(x.y.Z | x > 0)必須在只做了向下兼容的 bug 修復時才遞增。 - bug 修復的定義是,修改程序內部行為,使其輸出正確的結果
次版本號 Y(x.Y.z | x > 0)必須在有向下兼容的新功能出現時遞增。 - 在任何公共 API 的功能被標記為棄用時也必須遞增。也可以在內部程序有大量功能更新或改進時遞增。其中可能包括修訂號的改變。每當次版本號遞增時,修訂號必須置零。
主版本號 X(X.y.z | X > 0)必須在有任何不向下兼容的修改被加入公共 API 時遞增。其中可能包括次版本號及修訂號的改變。 - 每當主版本號遞增時,次版本號和修訂號必須置零
修改版本號的MR里需要描述此次版本更新和上一版本的區別內容
FQ:
-
Q:萬一不小心發布了不兼容的更新,但只更改了次版本號,該如何補救?
-
A:一旦發現自己破壞了語義化版本控制的規范,就要修正這個問題,並發行一個新的次版本號來更正這個問題,並且恢復向下兼容。即使是這種情況,也不能去修改已發行的版本。可以的話,將有問題的版本號記錄到文檔中,告訴使用者問題所在,讓他們能夠意識到這是有問題的版本。
-
Q:如何判斷發布 1.0.0 版本的時機?
-
A:當你的軟件被用於正式環境,它應該已經達到了 1.0.0 版。如果你已經有個穩定的 API 被使用者依賴,也應是 1.0.0 版。如果你有很多向下兼容的問題要考慮,也應該算是 1.0.0 版了。
-
Q:對於公共 API,若即使是最小但不向下兼容的改變都需要產生新的主版本號,豈不是很快就達到 42.0.0 版?
-
A:這是開發者的責任感和前瞻性的問題。不兼容的改變不應該輕易被加入到有許多依賴代碼的軟件中。升級所付出的代價必須是有意義的。要遞增主版本號來發行不兼容的版本,意味着你已經為這些改變所帶來的影響慎重考慮過,並且評估了所涉及的成本/效益比。
操作
項目每周迭代情況下,每次發布時增加次版本號,如果發布失敗重新修改后發布則增加修訂號。
package.js
{
"version": "1.0.0"
}
Makefile
tag:
@cat package.json | xargs -0 node -p 'JSON.parse(process.argv[1]).version' | xargs git tag
@git push origin --tags
提交版本號:
使用make命令
$make tag
或
// 刪除版本號
git tag -d 1.0.0
// 添加版本號
git tag 1.0.0
// 推送到git服務器
git push origin --tags
4、eslint配置規則
{
// 開啟了Node.js 和 Mocha 兩個環境,會自動識別其中的全局變量
"env": {
"node": true,
"mocha": true
},
"plugins": [],
"parserOptions": {
// 使用 ES2017 的語法
"ecmaVersion": 8,
"sourceType": "script",
"ecmaFeatures": {
}
},
"rules": {
// 強制駝峰法命名變量
"camelcase": ["error"],
// 不允許在條件判斷語句中使用賦值語句
"no-cond-assign": ["error"],
// 禁用行尾空格
"no-trailing-spaces": ["error"],
// 不允許在代碼里寫 console.xxx 語句
"no-console": ["error"],
// 不允許使用 var 聲明變量
"no-var": ["error"],
// 不能直接 return 賦值語句
"no-return-assign": ["error"],
// 不允許給函數參數重新賦值
"no-param-reassign": ["error"],
// 不允許注釋跟代碼在同一行
"no-inline-comments": ["error"],
// 逗號前面不允許有空格,逗號后面必須有空格
"comma-spacing": ["error", {"before": false, "after": true}],
// 強制始終使用分號結尾
"semi": ["error", "always"],
// 強制始終使用大括號,就算代碼塊中只有一條語句
"curly": ["error", "all"],
// 強制使用 === 和 !==
"eqeqeq": ["error", "always"],
// 大括號的風格,前面一個在行末,后面一個在行首
"brace-style": ["error"],
// 縮進格式為2個空格,switch語句的case子句也是縮進2個空格
"indent": ["error", 2, {"SwitchCase": 1}],
// 一行代碼的最大長度,tab計算為2個空格,忽略注釋部分
"max-len": ["error", 180, 2, {"ignoreComments": true}],
// 禁止出現未被使用過的變量
"no-unused-vars": ["error"],
// 可以使用的地方強制使用箭頭函數
"prefer-arrow-callback": ["error"],
// 強制統一使用單引號
"quotes": ["error", "single"],
// 強制在關鍵字前后使用空格
"keyword-spacing": ["error"],
// 冒號前面不允許有空格,冒號后面強制需要一個空格
"key-spacing": ["error"],
// 分號前不允許有空格,分號后強制需要空格
"semi-spacing": ["error", {"before": false, "after": true}],
// json 對象大括號前后不允許有空格
"object-curly-spacing": ["error", "never"],
// 操作符前后必須有空格
"space-infix-ops": ["error"],
// (左)大括號前強制空格
"space-before-blocks": ["error"]
},
"globals": {
}
}