前端开发规范白皮书

1 开发规范

1.1 命名规范

  • 变量
    • 使用小驼峰命名法,第一个单词开头小写,其余单词开头大写,如:userInfo
    • 变量名第一个单词需是名词
    • good: userInfo
    • bad:setName
  • 常量
    • 全部大写字母
    • 多个单词之间使用下划线连接
    • 以名词开头
  • 函数
  • 构造函数和类
    • 使用大驼峰命名法,如:User
    • 建议使用单个名词表达
    • 构造函数尽量使用class表达,function慎用
  • 私有成员变量成员方法
    • 私有成员使用下划线开头,小驼峰命名法
/**
 * @desc The Student class is manage student information
 * @author Ting Lei(this is optional)
 */
class Student {
  constructor(name) {
    /**
     * student name
     * @type {string} */
    this.name = name

    /**
     * student age
     * @type {number}
     */
    this._age = 0
  }

  /**
   * @desc return student name and age complex string
   * @returns {string}
   */
  getStudentInfo() {
    return this.name + this.age
  }

  //#region get&set
  get age() {
    return this._age
  }

  /**
   * @desc set student age
   * @param {number}
   */
  set age(val) {
    typeof val === 'number' && val && (this._age = val)
  }
  //#endregion
}

1.2 缩进规范

  • 左花括号不单独占行,右花括号单独占一行
  • 行结束推荐不使用分号

1.3 代码语法规范

  • 可使用ES11 stage-3以内的语法均可使用,兼容性在编译时使用babel-polyfill解决
  • 多用const少用let,var尽量不用
  • 使用单引号而不要使用双引号
  • 循环体内不申明function
  • 使用===,禁止使用==
  • 使用async await方式代替then地狱
  • 函数体内推荐使用反向判断,不符合条件return,而不是符合条件执行
  • 禁止修改函数入参对象内key值

1.4 规范工具

  • 使用ESLint对代码进行开发时检测
  • 使用Prettier对代码进行格式化

1.5 注释规范

  • 使用英文注释
  • @abstract (同义词: JSDoc @virtual 标签)描述这个成员必须在继承的子类中实现(或重写)。
  • @access 指定该成员的访问级别(私有 private,公共 public,或保护 protected)。
  • @alias 标记成员有一个别名。
  • @async Indicate that a function is asynchronous.
  • @augments (同义词: JSDoc @extends 标签)指名这个子类继承至哪个父类,后面需要加父类名。
  • @author 指定项目的作者。
  • @borrows 指明这个对象使用另一个对象的某些东西。
  • @callback 描述一个回调函数。
  • @class (同义词: JSDoc @constructor 标签)此函数旨在需要使用"new"关键字调用,即构造函数。
  • @classdesc 用于为类提供一个描述,这样和构造函数的描述区分开来.
  • @constant (同义词: JSDoc @const 标签)指明这个对象是一个常量。
  • @constructs 描述这个函数成员将成为类的构造函数。
  • @copyright 描述一个文件的版权信息。
  • @default (同义词: JSDoc @defaultvalue 标签)描述默认值.
  • @deprecated 标签指明一个标识在你代码中已经被弃用。
  • @description (同义词: JSDoc @desc 标签)描述一个标识符。
  • @enum 描述一个相关属性的集合。
  • @event 描述一个事件。
  • @example 提供一个如何使用描述项的例子。
  • @exports 标识一个由JavaScript模块导出的成员。
  • @external (同义词: JSDoc @host 标签)用来标识一个在当前包外部定义的类,命名空间,或模块。
  • @file (同义词: JSDoc @fileoverview 标签, JSDoc @overview 标签)描述一个文件。
  • @fires (同义词: JSDoc @emits 标签)描述事件这个方法可能会触发。
  • @function (同义词: JSDoc @func 标签, JSDoc @method 标签)描述一个函数或方法。
  • @generator Indicate that a function is a generator function.
  • @global 指定一个在文档的标识是为全局性的标识。
  • @hideconstructor Indicate that the constructor should not be displayed.
  • @ignore 忽略文档中的一个标识。
  • @implements 指示一个标识实现一个接口
  • @inheritdoc 指明这个标识应继承其父类的文档。
  • @inner 描述一个内部对象。
  • @instance 标明该标识符作为它父标识符的实例成员。
  • @interface 使一个标识符作为其他标识符的一个实现接口。
  • @kind 指明标识的类型
  • @lends 将一个字面量对象的所有属性标记为某个标识符(类或模块)的成员。
  • @license 标识你的代码采用何种软件许可协议。
  • @listens 指示一个标识监听指定的事件。
  • @member 标签 (同义词: JSDoc @var 标签)记录一个成员。
  • @memberof 标明这个标识属于哪个父级标识。
  • @mixes 此对象混入了另一个对象中的所有成员。
  • @mixin 记录一个mixin(混入)对象。
  • @module 记录一个 JavaScript 模块。
  • @name 记录一个对象的名称。
  • @namespace 记录一个命名空间对象。
  • @override 指明一个标识符覆盖其父类同名的标识符。
  • @package This symbol is meant to be package-private.
  • @param 标签 (同义词: JSDoc @arg 标签, JSDoc @argument 标签)记录传递给一个函数的参数。
  • @private 标记标识符为私有。
  • @property 标签 (同义词: JSDoc @prop 标签)记录一个对象的属性。
  • @protected 标记标识符为受保护的。
  • @public 标记为公开的。
  • @readonly 标记一个标识符为只读。
  • @requires 指示这个文件需要一个 JavaScript 模块。
  • @returns (同义词: JSDoc @return 标签)描述一个函数的返回值。
  • @see 指明可以参考另一个标识符的说明文档,或者一个外部资源。
  • @since 标签标明一个类,方法,或其它标识符是在哪个特定版本开始添加进来的。
  • @static 记录一个静态成员。
  • @summary 完整描述的一个简写版本。
  • @this 指明this关键字的指向
  • @throws (同义词: JSDoc @exception 标签)说明可能会被抛出什么样的错误.
  • @todo 记录要完成的任务。
  • @tutorial 插入一个到包含教程文件的链接。
  • @type 记录一个对象的类型。
  • @typedef 描述自定义类型.
  • @variation 区分具有相同名称的不同的对象。
  • @version 指明被用于表示该项的版本。
  • @yields (同义词: JSDoc @yield 标签)Document the value yielded by a generator function.

内联标签(Inline Tags)

  • @link (同义词: JSDoc {@linkcode}, JSDoc {@linkplain})链接到文档中的其他项。
  • @tutorial链接到一份教程。

2 Vue编码风格规范(推荐)

参考 风格指南 Vue.js 中优先级A(必要的)和优先级B(强烈推荐)

3 Git提交规范

Commit 规范我们统一遵循使用 Angular 团队提议的《AngularJS Git Commit Message Conventions》 格式如下: <type>(<scope>): <subject> 必须填写 空一行 <body> 选填 空一行 <footer> 选填

  • type 类型有如下,只能从一下列表选择
    • build: 影响生成系统或外部依赖性的更改
    • ci: 更改 CI 配置文件和脚本
    • feat: 新功能(feature)
    • fix: 修补 bug
    • perf: 提高性能的代码更改
    • docs:  文档(documentation)
    • style: 不影响代码含义的更改(不影响代码运行的变动)
    • refactor: 代码修改既不修复错误,也不添加特征(即不是新增功能,也不是修改 bug 的代码变动)
    • test: 添加缺失测试或纠正现有测试
    • revert: 撤回
  • scope 影响范围
  • subject 是 commit 目的的简短描述,不超过 72 个字符
  • body Body 部分是对本次 commit 的详细描述,可以分成多行
  • footer 不兼容变动主要及一些额外说明

例子 1

feat($browser): onUrlChange event (popstate/hashchange/polling)

Added new event to $browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)

例子 2

fix($compile): couple of unit tests for IE9

Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.

Closes #392
Breaks foo.bar api, foo.baz should be used instead

4 工程管理规范(建议)

4.1 源代码管理模式

multirepo 优点:

  • 单工程包体积较小,可独立构建独立发布
  • 代码量少,参与人数少因此解决问题复杂度降低 缺点:
  • 代码复用率低,同一个项目不同的包中,会有大量冗余代码
  • 版本管理困难,例如A被B,C依赖并且B,C依赖A的版本不同,如果A升级会导致不可预测的错误发生
  • 项目基建复杂,每个包都有自己的基建系统,多出维护会加大维护工作量 monorepo 优点:
  • 项目基建统一管理,在每个package中都复用,CI/CD只需要做一次
  • 错误感知力强,在一个package出现bug,全体依赖都能立即感知报错
  • 团队协作容易,相同的git管理模式,相同的代码规范
  • 代码冗余小,对于冗余计算,随时通过重构的方式进行模块划分
  • 重构成本低 缺点:
  • 工程体积大

4.2 依赖管理工具

本项目计划采用pnpm工具对依赖包进行管理,pnpm和yarn及npm一样,都是依赖包管理工具,其管理模式更符合monorepo类型的项目。 pnpm的特点:

  • 包安装速度极快
  • 磁盘利用率非常高
  • 依赖引用安全

安装速度快和磁盘利用率高都和其对依赖包的管理方式相关,pnpm采用依赖包扁平管理+软连接引用的方式,这种管理的好处是,相同版本的包不会被安装第二次,所有的依赖包都在.pnpm/<package-name>@version/node_modules中。 依赖引用安全指的是在yarn和npm中A依赖B,B依赖C,那么A中可以直接使用C(依赖提升)。 问题在于

  • B 的版本是可能随时变化的,假如之前依赖的是C@1.0.1,现在发了新版,新版本的 B 依赖 C@2.0.1,那么在项目 A 当中 npm/yarn install 之后,装上的是 2.0.1 版本的 C,而 A 当中用的还是 C 当中旧版的 API,可能就直接报错了。
  • 如果 B 更新之后,可能不需要 C 了,那么安装依赖的时候,C 都不会装到 node_modules里面,A 当中引用 C 的代码直接报错。 而pnpm通过软连接方式管理依赖包就规避了这个问题,凡是没在package.json中生命依赖的都无法import使用。

5 编辑器

6. 代码安全规范

6.1 安全摘要

  • 前、后端进行必要的安全校验
  • 密码的强度、防暴力破解、验证码功能
  • 防SQL注入,元字符、特殊字符的过滤,后端 JDBC用预处理的方式
  • nginx 、tomcat配置安全头文件、SSL证书
  • 上传文件,进行文件类型的前、后端的校验,MIME类型检测

6.2 Web安全规范

开发安全的Web应用程序是很有挑战性的,主要原因如下:

  • 由于用户可以很容易的访问应用程序,恶意用户也可以这样做。
  • HTTP 不是为应用设计的,自然更不是为安全应用设计的。HTTP 的全问题与标准C库中的字符串函数带来的缓冲区溢出问题一样麻烦。程序员需要通过自己的方法来确认应用是安全的。
  • 应用不只需要防御恶意用户,还需要帮助正常用户防御恶意用户的攻击。
  • 本节覆盖了创建安全Web应用最重要的方面,包括:会话,SSL,认证等方面。

使用POST 不能使用GET

使用Get 方法传递的参数将包含在URL 里,这些信息会被记录在日志文件中或通过HTTP 头的Referrer 发送到其它站点上,被存储在浏览器的历史记录中。当参数中包含重要信息时,这些信息也会被记录,这将增大泄密的风险。 禁止使用Get方法同时可以防止很多跨站脚本攻击漏洞,这样可以使攻击者无法向用户发送含有恶意Get参数的URL。

使用SSL进行通信

应合理的使用SSL,它能对嗅探攻击和中间人攻击做出有效的防御,同时还可以为主机和客户机提供可信的验证。 不应将SSL做为可选的方案。一个安全的程序不能在使用443端口(SSL通信端口)的同时也接受80端口(一般的HTTP服务端口)请求。

假设浏览器已被监控

要求在验证之前不相信来自客户端的任何数据,包括那些不常被客户修改的值(如cookies, 被隐藏的域)。因为恶意用户可以轻易伪造cookie 或者隐藏表单,对客户习惯做出的任何假设都有可能会被用来对程序发起攻击。

需要在服务端进行安全验证

要求在服务器端进行安全验证。 因为攻击者很容易就可以绕过用户设定的那些请求页面和浏览器,直接与应用服务器通信,这就意味着攻击者可以绕过在客户端所做的任何设置,如JavaScript 验证逻辑。JavaScript可以帮助合法的用户对不正常的输入信息进行检测,但是不能确保服务器接收到数据的安全性。

不能依赖Request到达的顺序

不能依赖Request到达的顺序。因为攻击者可以随意控制request 到达的顺序来适合自己的需要。例如,如果程序通过不同页面来搜集信息,在较早的表单里验证了信息,而在修改信息的表单则没有进行验证,那么攻击这就可以利用后者来绕过输入验证。

要求在恶意环境中保护浏览器

不能让出现问题的浏览器成为攻击者可以利用的工具,来攻击存在漏洞的客户端。这一问题可以理解为输入验证的问题,但是从深度防御的角度考虑,它也是一个输出编码(加密)的问题。例如,可以通过对HTTP请求进行校验来防御来自外部的跨站脚本攻击,如果对HTTP应答都进行了编码,这样就可以防御来自外部和内部的跨站脚本。

要求创建默认的错误页面

应为HTTP错误创建一个默认的错误页面,丢弃所有的异常,防止攻击者从应用程序的默认出错页面中得到系统信息。

要求使用通用错误消息

应构造错误提示信息来防止诸如用户id 、网络、应用程序以及服务器环境的细节等重要敏感信息的泄漏。包括:

  • 不区分错误的用户名和错误的密码;
  • 在返回的报告中不能包含主机信息、网络DNS信息、软件版本信息、错误代码或者其它错误的详细信息;
  • 不允许把错误的细节放在错误页面的注释里。

应使用强的会话ID

会话id 长度不能低于64位,建议使用128位长度的会话id。 要求在确定session 的id长度以及生成id的随机种子之前,不相信web程序的容器。因为会话id太短很容易被暴力破解。如果攻击者猜测到授权用户的会话id就可以接管用户的会话。

在每次认证后创建一个新的会话

在用户认证成功后应先将之前的用户会话销毁,然后重新为用户生成新的会话,保证认证成功前后,用户会话ID不一样。

应保护Cookie

要求在建立Session时用户以SSL方式连接,并且将该会话的安全标识设置为cookie 只能通过安全连接进行传输。 不能只依赖HttpOnly cookie带来的任何安全保障,因为其只限于在IE下使用,攻击者也可以很方便的窃取到它。

限制会话最大空闲时间

应用程序中可以将会话最大空闲时间设置为配置参数。限制会话最大空闲时间可以带来如下的好处:

  • 缩短未能及时注销的用户暴露在外的时间;
  • 减少可供攻击者破解的session id 的平均时间。

限制会话最大持续时间

应用程序中可以通过限制会话最大持续时间来增加应用程序的安全性和稳定性。当一个会话超过了会话id最大持续时间的时候,需要通过重新认证来继续进行此次会话。这样可以有效的防止攻击者窃取会话id。

允许用户终止其会话

在应用程序中允许用户通过注销来保护自己的帐号,允许用户终止其会话能够带来如下好处:

  • 在公共设备上的用户只能通过这种方法才能防止在这个设备上的后来使用的用户访问其帐户。
  • 允许用户终止会话,可以防止后来取得该台电脑控制权的攻击者攻击其帐户。
  • 通过结束不使用的session,可以减少可供攻击者猜解的会话 id 的平均次数。

应在会话终止时清空数据

不允许会话数据在会话终止后继续存在,否则该数据会被攻击者利用,应在会话终止时立即销毁所有会话数据。