zoukankan      html  css  js  c++  java
  • 代码规范、API设计等规范

    一份整理好了的规范文档,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';

    注释规范

    1. 行内注释:行内注释以两个斜线开始,以行尾结束。双斜线与代码以及注释文字之间都都保留一个空格。
    function addNumber() {
    return 3 + 2; // 5
    }
    
    1. 单行注释:单独一行,单行注释以两个斜线开始,以行尾结束。
    function addNumber(a, b) {
    return a + b;
    }
    
    // 调用求和方法
    addNumber(2, 3);
    
    
    1. 方法注释:函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求。
      /**
    • 方法描述
    • @param {type} 参数值 参数说明
    • @param {type} 参数值 参数说明
      /
      示例:
      /
      *
    • @param {number} a 参数a
    • @param {Number} b 参数b
      */
      funtion addNumber(a, b) {
      return a + b;
      }

    格式规范

    • JS格式规范要严格遵循 Eslint,要求在 husky 钩子中增加Eslint规则校验,校验不通过,代码提交失败。

    其他规范

    1. 变量声明
    • 对所有的变量优先使用const,可变的变量使用let,不允许使用var
    // good
    const a = 1;
    const b = 2;
    
    // good
    let count = 1;
    if (true) {
        count += 1;
    }
    
    1. Strings
    • 字符串使用单引号 ‘’
      生成字符串时,使用模板字符串代替字符串连接
    // bad
    function sayHi(name) {
      return 'How are you, ' + name + '?';
    }
    
    // good
    function sayHi(name) {
      return `How are you, ${name}?`;
    }
    
    1. 对象
    • 使用字面值创建对象
    // 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
    };
    
    1. 数组
    • 使用字面值创建数组
    // 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];
    
    1. 解构
    • 使用解构存取和使用多属性对象
    // 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}`;
    }
    
    1. 函数
    • 尽可能使用箭头函数
      不允许在一个非函数代码块(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);
      };
    }
    

    语法原则

    1. 统一使用Node.js支持的 ES6 及以上版本的新特性写法
    2. 尽量使用常用常规的方法去实现一个功能,避免使用各种奇技淫巧
    3. 能用原生语法或原生函数实现的功能尽量不要借助 lodash 等额外的库来实现
    4. 统一使用 Promise + async/await 取代 callback, 解决回调地狱
    5. require() 引入的模块和库按代码长度排序,且库和自定义模块分开,库引用在前,自定义模块在后
    6. 对象定义统一用 {} 定义,而不是 new Object()
    7. 使用字面值创建数组,数组定义统一用 [],而不是 new Array()
    8. 向数组添加元素时使用 push 替代直接赋值
    9. 使用拓展运算符 ... 复制数组
    10. 使用 map、reduce、filter 时如果函数简单应省略 return
    11. 给注释增加 FIXME 或 TODO 的前缀,帮助其他开发者快速了解这是一个需要复查的问题,或是给需要实现的功能提供一个解决方式。

    命名规范:

    1. 函数名/变量名:
      方式: 小驼峰
      good: testFunction/testVar
      bad: test_function/test_var
    2. 全局常量:
      方式: 大写下划线分隔
      good: TEST_CONST
      bad: test_const
    3. 文件名:
      方式: 小写下划线分隔
      good: test_file.js
      bad: test-file.js
    4. 类名:
      方式: 小写下划线分隔
      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])
    1. GET 读取资源(必须保证接口的幂等性)
    2. POST 新增资源
    3. PUT 更新资源
    4. DELETE 删除资源
    • 资源的描述尽量采用复数形式
    good: GET /api/v1/dubhe/tasks
    bad: GET /api/v1/dubhe/task_list
    
    • 对于不方便直接抽象为资源的操作,例如 启动一个任务 可采用如下动词的方式
    POST /api/v1/dubhe/start/task/:taskId
    
    • 参数传递主要使用如下三种方式(文件上传除外)
    1. 路由参数(Route Param)
    2. URL查询参数(Query String)
    3. 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

    • 版本号修改规则:
    1. 主版本号:当你做了不兼容的API修改的情况需要增加
    2. 次版本号:当你做了向下兼容的功能性新增需要增加
    3. 修订号 :当你做了向下兼容的问题修正需要增加
      基本规范
    4. 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。
    5. 1.0.0 的版本号用于标识稳定 API 的发布。这一版本之后,所有的版本号更新都基于公共 API 的修改内容
    6. 带版本号的软件发行后,禁止改变该版本软件的内容。任何修改都必须以新版本发行。
      修订号 Z(x.y.Z | x > 0)必须在只做了向下兼容的 bug 修复时才递增。
    7. bug 修复的定义是,修改程序内部行为,使其输出正确的结果
      次版本号 Y(x.Y.z | x > 0)必须在有向下兼容的新功能出现时递增。
    8. 在任何公共 API 的功能被标记为弃用时也必须递增。也可以在内部程序有大量功能更新或改进时递增。其中可能包括修订号的改变。每当次版本号递增时,修订号必须置零。
      主版本号 X(X.y.z | X > 0)必须在有任何不向下兼容的修改被加入公共 API 时递增。其中可能包括次版本号及修订号的改变。
    9. 每当主版本号递增时,次版本号和修订号必须置零
      修改版本号的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": {
      }
    }
    
  • 相关阅读:
    NTP on FreeBSD 12.1
    Set proxy server on FreeBSD 12.1
    win32 disk imager使用后u盘容量恢复
    How to install Google Chrome Browser on Kali Linux
    Set NTP Service and timezone on Kali Linux
    Set static IP address and DNS on FreeBSD
    github博客标题显示不了可能是标题包含 特殊符号比如 : (冒号)
    server certificate verification failed. CAfile: none CRLfile: none
    删除文件和目录(彻底的)
    如何在Curl中使用Socks5代理
  • 原文地址:https://www.cnblogs.com/mapleChain/p/11527902.html
Copyright © 2011-2022 走看看