关于编码规范的分享

315 阅读5分钟

什么是编码规范?

代码规范是指程序员在编码时要遵守的规则,规范的目的就是为了让程序员编写易于阅读、可维护的代码

为什么要制定一个属于团队的编码规范?

  • 规范的代码可以促进团队合作,方便大家更好的接手彼此的代码
    因为几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护
  • 规范的代码可以降低开发和维护成本
    可以让团队成员尽快地理解业务代码,更好的去开发和维护

具体的编码规范

总体约定

坚持组织应用的结构,力求:快速定位 (Locate) 代码、一眼识别 (Identify) 代码、 保持扁平结构 (Flattest) 和尝试 (Try) 遵循 DRY (Do Not Repeat Yourself, 不重复自己) 原则。

四项基本原则定义文件结构,上面的原则是按重要顺序排列的。

快速定位

坚持直观、简单和快速地定位代码。
为何?
要想高效的工作,就必须能迅速找到文件,特别是当不知道(或不记得)文件时。 把相关的文件一起放在一个直观的位置可以节省时间。

一眼识别

坚持命名文件到这个程度:看到名字立刻知道它包含了什么,代表了什么
为何?
花费更少的时间来查找和琢磨代码,就会变得更有效率。

保持扁平结构

为何?
没人想要在过多层级的目录中查找文件。扁平的结构有利于搜索。
举一个例子
在 A 页面中需要 B , C 两个组件,而 B 又需要 D E 组件,E组件又需要F组件的支撑
错误示范

erDiagram
A ||--o{ B : contains
B ||--|{ D : contains
B ||--|{ E : contains
E ||--|{ F : contains
A ||--o{ C : contains

正确示范

erDiagram
A ||--o{ B : contains
A ||--o{ D : contains
A ||--o{ E : contains
A ||--o{ F : contains
A ||--o{ C : contains

为何?
这种结构也更有利于组件的提取,方便其他组件复用

T-DRY(尽量不重复自己)

坚持DRY(Don't Repeat Yourself,不重复自己)。
避免过度 DRY,以致牺牲了阅读性。

为何?
虽然 DRY 很重要,但如果要以牺牲 LIFT 的其它原则为代价,那就不值得了。 这也就是为什么它被称为 T-DRY

文件结构约定

以react举例,项目文件总览如下
image.png

  • api 项目中的前后端交互的接口
  • component 项目中通用的组件
  • hooks 项目中用到的hooks
  • router 项目中的路由
  • stores 项目中的数据管理
  • theme 项目中的主题
  • utils 项目中通用的方法,常量,正则等

页面详细分布如下:
image.png
里面包含如下信息:
- 页面SpecialFund下面有两个子业务,分别是IndexPageNewKineticFunds
- NewKineticFunds下面的主流程在 index.js中,所需组件在components下,分别是 DataBoardDataStatistics
- 在 DataBoard 中主流程在 index.js,所需常量在constant.js,所需方法在utils.js

然后在实际操作中,当我们发现有多个组件共用一个方法或常量时,我们就可以把它提取到项目根目录中的utils中

这样的话,后续过来接手的人,无论是过来开发还是维护都能够很轻松的找到要修改的地方,大大的提升开发效率和开发体验

单一职责

单一规则

坚持每个文件只定义一样东西,考虑把文件大小限制在 200 行代码以内
为何?
单组件文件非常容易阅读、维护,并能防止在版本控制系统里与团队冲突。
为何?
单组件文件可以防止一些隐蔽的程序缺陷,当把多个组件合写在同一个文件中时,可能造成共享变量、创建意外的闭包,或者与依赖之间产生意外耦合等情况 为何?
让代码更加可复用、更容易阅读,减少出错的可能性。

最好将组件及其支撑部件重新分配到独立的文件
如将组件中的常量提取到constant.js中,将组件中的方法提取到utils.js

小函数

坚持定义简单函数,考虑限制在 30 行之内。
为何?
简单函数更易于测试,特别是当它们只做一件事,只为一个目的服务时。
为何?
简单函数促进代码复用。
为何?
简单函数更易于阅读,同时也更容易维护。

命名

总体命名

坚持所有符号使用一致的命名规则,并遵循同一个模式来描述符号的特性和类型
为何?
命名约定提供了一致的方式来查找内容,让你一眼就能找到
为何?
命名约定帮助你更快得找到想找的代码,也更容易理解它

文件命名

坚持为所有东西使用一致的命名约定,以它们所代表的东西命名。
为何?
遵循一致的约定可以快速识别和引用不同类型,方便我们快速的识别文件中有什么
如:

  1. constant 表示常量,则里面的内容都是常量
  2. utils 表示工具,里面的内容都是方法
  3. interceptor 表示拦截,则里面的内容都是属于接口拦截器的定义
  4. regex 表示正则,则里面的内容都是正则表达式
    ...等等

为何?
类型名可以让你轻松利用编辑器或者 IDE 的模糊搜索功能找到特定文件类型。

代码细则

组件内部涉及到异步操作,里面必须添加回调或返回promise
为何?
方便其他人在调用时,知晓异步操作何时完成,并进行后续操作
错误示范

export const loadMapScript = (funcName = 'initMap') => {
  const script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = `https://map.qq.com/api/gljs?v=1.exp&key=${TENXUMAP_KEY}&callback=${funcName}&libraries=tools,service`;
  document.body.appendChild(script);
};

正确示范

export const loadMapScriptP = (funcName = 'initMap') => {
  return new Promise((resolve, reject) => {
    const mapId = 'tx_map';
    const mapScriptEle = document.getElementById(mapId);
    if (mapScriptEle || window.TMap) {
      return resolve();
    }
    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.id = mapId;
    script.src = `https://map.qq.com/api/gljs?v=1.exp&key=${TENXUMAP_KEY}&callback=${funcName}&libraries=tools,service`;
    script.onload = () => {
      resolve();
    };
    script.onerror = () => {
      reject();
    };
    document.body.appendChild(script);
  });
};

推荐多行注释
在编写方法时,若有必要添加注释,推荐多行注释
为何?
方便调用时,一眼看出该方法的用途,而单行注释没有被IDE更好的展示
举例

image.png

参考资料

百度前端代码规范 www.bookstack.cn/read/ecomfe…
Angular风格指南 angular.cn/guide/style…