19 KiB
中国电信软件研发规范编码规范(前端分册)
脱敏整理版:已移除编制人员、联系人和联系方式,并合并无意义硬换行。技术条款、章节和示例以原始 DOCX 为争议核验依据。
中国电信软件研发规范编码规范 (前端分册) (修订版)
中国电信集团有限公司
2023 年 12 月
i
编制人员信息已移除。
版本变更历史
- 文档说明
1.1 编制说明
软件行业的高速发展, 对软件开发者的综合素质要求越来越高, 不仅仅是编程知识点,其他维度知识点也会影响最后的交付质量,本文档以开发前端项目角度,详细描写了前端的代码规范,分别从 HTML、CSS、JavaScript、TypeScript、四个方面入手,并且每个章节进行了详细划分,方便读者能快速定位,规范自己的代码, 提高项目代码质量。
1.2 适用范围
本规范适用于指导中国电信软件研发工作。
1.3 起草单位
本规范的起草单位是中国电信集团公司。
1.4 解释权
本规范解释权属于中国电信集团公司。
1.5 版权
本规范的版权属于中国电信集团公司。
1.6 名词解释
- 前端研发规范
2.1. HTML 编码规范
2.1.1. 文档类型
- 【强制】使用 HTML5 DOCTYPE。
2.1.2. 语言
- 【推荐】指定 html 标签上的 lang 属性。
2.1.3. 元数据
- 【推荐】使用 UTF-8 字符编码。
声明一个明确的字符编码, 可以让浏览器更快速高效地确定适合网页内容的渲染方式。由于历史原因, 不同浏览器采用了不同的字符编码 。但对于新业务,如无特殊要求, 统一使用 UTF-8 字符编码, 以便统一。在 HTML 中使用 声明文档的编码方式:
- 【推荐】页面提供给移动设备使用时, 需要设置 viewport。
2.1.4. 资源加载
- 【推荐】引入 CSS 和 JavaScript 时无需指定 type 。 根据 HTML5 规范,引入 CSS 和 JavaScript 时通常不需要指明 type, 因为 text/css 和
text/javascript 分别是他们的默认值。
- 【推荐】在 head 标签内引入 CSS, 在 body 结束标签前引入 JS。
在 中指定外部样式表和嵌入式样式块可能会导致页面的重排和重绘, 对页面的渲染造成影响 。 因此, 一般情况下, CSS 应在<head></head> 标签里引入。
2.1.5. 页面标题
- 【强制】页面需要指定 title 标签, 有且仅有 1 个。
2.1.6. 编码风格
-
【推荐】统一使用 2 个空格缩进, 不要使用 4 个空格或 tab 缩进。
-
【强制】在 HTML 注释代码中, 不允许出现任何敏感信息。
-
【推荐】单行注释, 需在注释内容和注释符之间需留有一个空格, 以增强可读性。
-
【推荐】多行注释, 注释符单独占一行, 注释内容 2 个空格缩进。
2.1.7. 标签
-
【强制】标签名统一使用小写。
-
【推荐】不要省略自闭合标签结尾处的斜线,且斜线前需留有一个空格。
2.1.8. 属性
-
【强制】属性值使用双引号, 不要使用单引号。
-
【推荐】不要为 Boolean 属性添加取值。
XHTML 需要每个属性声明取值,但是 HTML5 并不需要。一个元素中Boolean 属性存在即表示取值 true, 不存在则表示取值 false
- 【推荐】 自定义属性的命名: 以 data- 为前缀。
2.1.9. 语义化
- 【参考】尽量根据语义使用 HTML 标签。
2.2. CSS 编码规范
2.2.1. 文件引用
-
【强制】一律使用link 的方式调用外部样式。
-
【推荐】 不要在 <style> 块中使用 @import; 不要在页面中使用 <style> 块。
2.2.2. 命名-组成元素
-
【强制】命名必须由字母 、 中划线或数字组成且不能以数字或中划线开头。
-
【强制】不允许使用拼音与英文的混合命名, 更不允许直接使用中文的方式; 禁止同一个含义的内容, 在同一个应用中出现多种不同的单词与翻译。
2.2.3. 命名-词汇规范
-
【参考】不依据表现形式来命名。
-
【参考】可根据内容来命名, 可根据功能来命名。
2.2.4. 命名-缩写
-
【强制】保证缩写后还能较为清晰保持原单词所能表述的意思。
-
【推荐】使用业界熟知的或者约定俗成的来定义 CSS 类名。
2.2.5. 编码风格
-
【强制】所有声明都应该以分号结尾, 不能省略。
-
【推荐】使用 2 个空格缩进, 不要使用 4 个空格或 tab 缩进。
-
【推荐】选择器和 { 之间保留一个空格。
-
【推荐】属性名和 : 之前无空格, : 和属性值之间保留一个空格。
-
【推荐】> 、+ 、~ 、|| 等组合器前后各保留一个空格。
-
【推荐】在使用 , 分隔的属性值中, , 之后保留一个空格。
-
【推荐】注释内容和注释符之间留有一个空格。
-
【推荐】声明块的右大括号 } 应单独成行。
-
【推荐】属性声明应单独成行。
-
【推荐】单行代码最多不要超过 100 个字符。
-
【参考】使用多个选择器时, 每个选择器应该单独成行。
-
【参考】声明块内只有一条语句时, 也应该写成多行。
-
【参考】注释行上方需留有一行空行, 除非上一行是注释或块的顶部。
2.2.6. 选择器
- 【参考】不要使用 id 选择器。
id 会带来过高的选择器优先级, 使得后续很难进行样式覆盖(继而引发使用 !important 覆盖样式的恶性循环) 。
- 【参考】属性选择器的值始终用双引号包裹。
2.2.7. 属性和属性值
-
【推荐】使用尽可能短的十六进制值。
-
【推荐】不要使用 !important 重写样式。
-
【推荐】十六进制值统一使用小写字母(小写字母更容易分辨) 。
-
【推荐】长度值为 0 时, 省略掉长度单位。
-
【参考】保留小数点前的 0。
-
【参考】属性声明的顺序。
相关联的属性声明最好写成一组, 并按如下顺序排序:
1 定位: 如 position 、left 、right 、top 、bottom 、z-index
2 盒模型: 如 display、float、width、height、margin、padding、border
3 文字排版: 如 font 、color 、line-height 、text-align
4 外观: 如 background
5 其他属性
2.3. JavaScript 编码规范
2.3.1. 对象
-
【推荐】使用字面语法来创建对象。
-
【推荐】使用对象方法的缩写。
-
【推荐】 使用属性值的缩写。
-
【参考】避免直接调用 Object.prototype 的方法。
避免直接调用 Object.prototype 的方法, 例如 hasOwnProperty、 propertyIsEnumerable 、isPrototypeOf。这些方法可能会被对象上的属性覆盖, 导致错误。
- 【推荐】使用扩展运算符...处理对象,替代 Object.assgin 方法,来进行对象的浅拷贝。
2.3.2. 数组
-
【推荐】使用字面语法来创建数组。
-
【强制】某些数组方法的回调函数中必须包含 return 语句。
以下数组方法:map, filter, from , every, find, findIndex, reduce, reduceRight, some, sort 的回调函数中必须包含 return 语句, 否则可能会产生误用或错误。一个常见的误用是, 本该用 forEach 的场景却用了 map:
- 【推荐】使用扩展运算符 ... 处理数组。
ES6 提供了扩展运算符 ..., 可以简化一些数组操作。数组复制:
将类数组结构(有 Iterator 接口的对象)转换为数组:
数组拼接:
用 ... 替代 apply
特殊的,遍历可迭代对象时,使用 Array.from 而不是 ..., 以免创建一个临时数组:
- 【推荐】使用解构获取数组元素。
2.3.3. 解构
-
【推荐】在访问和使用对象的多个属性的时候使用对象的解构。
-
【推荐】对于多个返回值, 推荐使用对象解构而不是数组解构。
2.3.4. 字符
-
【强制】使用单引号和反引号定义字符串。
-
【推荐】单行最大输入数 100。
-
【推荐】文件最大行数 1000。
-
【推荐】 函数最大行数 80。
-
【推荐】拼接字符串使用模版字符串。
-
【推荐】模版字符串可以加入表达式。
2.3.5. 方法
-
【强制】永远不要定义一个参数为arguments,这将会优于每个函数给定范围的 arguments 对象。
-
【强制】不要用 Function 构造函数创建函数。
使用 new Function 创建函数会像 eval() 方法一样执行字符串,带来安全隐患。
- 【强制】不要在块中使用函数声明。
在非函数块(如 if、while 等) 中, 不要使用函数声明:
-
【参考】使用函数表达式替代函数声明。
-
【强制】不要使用 arguments 对象。
不要使用 arguments 对象, 使用剩余参数操作符 ... 代替。ES6 提供了 rest 操作符 ..., 与 arguments 相比可以更清晰地聚合函数的剩余参数 。此外, ... 得到的是一个真正的数组, 而 arguments 得到
的则是类数组结构。
-
【推荐】使用默认参数语法。
-
【参考】 函数的复杂度不应过高。
圈复杂度不超过 10。认知复杂度不超过 15。
- 【强制】将立即执行函数表达式(IIFE) 用小括号包裹。
2.3.6. 箭头函数
-
【推荐】 当你需要用匿名函数的时候, 使用箭头函数来替代他。
-
【推荐】箭头函数编码风格。
箭头函数参数的小括号 、函数体的大括号在某些时候可以省略,这可能导致风格的不统一, 因此需要规范其编码风格。l 函数体风格当函数体只包含一条 return 语句时, 可以省略函数体大括号和return, 以使代码更简洁。我们推荐使用这个 ES6 提供的语法糖, 它可以让书写和阅读更简洁。但你也可以选择始终加上大括号和 return, 以方便后续在函数体内增加语句。
当 return 的内容为对象或者有多行时, 需要用小括号包裹:
l 函数参数风格当函数只有一个参数, 且函数体为 return 简写语法时, 可以省略包裹参数的小括号以使代码更简洁。
我们建议仅在这种情况下省略包裹参数的小括号,其余情况都不要省略小括号 。但你也可以选择始终加上小括号, 以方便后续可能要增加参数。
2.3.7. 类和构造器
-
【推荐】尽量使用 class 来避免操作 prototype。
-
【推荐】使用 extends 语句进行类的继承。
extends 是用于原型继承的内建方法, 不会破坏 instanceof。
- 【强制】避免不必要的 constructor。
ES6 class 会提供一个默认的 constructor, 空 constructor 或者只调用父类的constructor 是不必要的。
2.3.8. 模块
-
【推荐】使用 ES6modules 而非其他非标准的模块系统。
-
【强制】不要用多个 import 引入同一模块。
-
【强制】import 语句需要放到模块的最上方。
-
【强制】禁止 default import 的名字跟文件内的其他 export 命名相同。
-
【强制】禁止引用自身。
-
【强制】禁止循环引用。
-
【参考】import 语句的排序。
import 语句建议按以下规则排序:先 import 第三方模块, 再 import 自己工程里的模块先 import 绝对路径, 再 import 相对路径
2.3.9. 迭代器和发生器
- 【参考】尽量使用 JavaScript 高阶函数来替代 for-in 和 for-of
需要迭代运算时,应优先使用 JS 提供的高阶函数,减少直接使用 for 循环(包括 for-in 和 for-of) 。如使用 map() / every() / filter() / find() / findIndex() / reduce() / some() / ... 来迭代数组, 使用 Object.keys() / Object.values() / Object.entries() 方法来迭
代对象。
- 【强制】不要使用发生器, 因为它们不适配es5。
2.3.10. 属性
-
【强制】访问属性时使用点符号。
-
【强制】访问变量属性时, 使用[]表示法。
2.3.11. 变量
- 【强制】使用 const 或者 let 来定义变量,避免因创建全局变量而污染全局命名空间。
2.3.12. 比较运算符和符号
- 【强制】使用 === 和 !== 来替代 == 和 !=
非严格相等运算符(== 和 !=)会在比较前将被比较值转换为相同类型,对于不熟悉 JS 语言特性的人来说, 这可能造成不小的隐患。因此,一般情况下我们应该使用严格比较运算符( === 和 !==)进行比较 。如果要比较的两个值类型不同,应该显性地将其转换成相同类型再进行严格比较,而不是依赖于 == 和 != 的隐式类型转换。
-
【强制】避免嵌套的三元表达式。
-
【强制】避免不必要的三元表达式。
2.3.13. 块
-
【推荐】 当有多行代码块的时候, 使用大括号包裹。
-
【强制】不要使用空代码块。
-
【强制】对于非空代码块, 采用 Egyptian Brackets 风格。
对于非空的代码块, 大括号的换行方式采用 Egyptian Brackets 风格,具体规则如下:左大括号 { 前面不换行, 后面换行右大括号 } 前面换行右大括号 } 后面是否换行有两种情况:如果 } 终结了整个语句, 如条件语句 、函数或类的主体, 则需要换行如果 } 后面存在 else、catch、while 等语句, 或存在逗号、分号、右小括号()), 则不需要换行
2.3.14. 控制语句
-
【强制】switch 语句中的 case 需要以 break 结尾。
-
【参考】控制语句的嵌套层级不要过深。
控制语句的嵌套层级不要超过 4 级, 否则将难以阅读和维护:
- 【强制】for 循环中的计数器应朝着正确方向移动。
当 for 循环中更新子句的计数器朝着错误的方向移动时,循环的终止条件将永远无法达到,这会导致死循环的出现。这时要么是程序出现了错误,要么应将for 循环改为 while 循环。
2.3.15. 注释
-
【推荐】使用/**...*/来进行多行注释。
-
【推荐】使用 // 进行单行注释。
-
【强制】注释内容和注释符之间需要有一个空格。
2.3.16. 代码风格
- 【强制】使用 2 个空格缩进。
2.3.17. 逗号
- 【强制】逗号不能放在行首。
2.3.18. 分号
- 【推荐】添加尾随分号 。。
2.3.19. 命名规范
-
【强制】避免单字母的名字 。使用有意义 、能描述功能的名字。
-
【参考】文件名: 使用小写字母命名。
考虑到部分操作系统(如 Windows, MacOS) 下文件系统大小写不敏感, 推荐使用 - 连接 。 例如: hello-world.js。
-
【参考】使用小驼峰(camelCase) 命名原始类型 、对象 、函数 、实例。
-
【强制】使用大驼峰(PascalCase) 命名类和构造函数。
-
【参考】命名不要以下划线开头或结尾。
2.3.20. 存取器
- 【强制】不要使用 JavaScript 的 getters/setters 方法, 因为它们会导致意外的副作用, 并且更加难以测试 、维护和推敲 。 相应的, 如果你需要存取函数的时候使用 getVal() 和 setVal('hello')。
2.4. TypeScript 编码规范
2.4.1. 代码风格
-
【强制】interface/type 类型中使用一致的成员分隔符分号。
-
【强制】块开始和结束不能空行。
-
【推荐】TS/TSX 在文件中字符串字面量使用单引号或反引号包裹。
-
【推荐】 加号 + 连接的两侧同为数字或同为字符串。
数字与字符串的连接往往会导致一些预期外的问题。
-
【强制】 即使 if/else/for/while 的语句只有一句, 也不得省略大括号。
-
【强制】类型声明时应正确添加空格间距。
TypeScript 类型声明周围添加合适的间距可以有效的提升代码可读性,我们约定:冒号前无空格, 冒号后保留一个空格箭头前后都保留一个空格
- 【强制】禁止使用三斜杠语法 /// 导入文件。
三斜杠语法已经被废弃, 声明文件(d.ts) 以外禁止使用。
2.4.2. 类
-
【参考】为类成员声明可访问类型。
-
【参考】类成员建议以固定的先后顺序排列。
类的静态方法 / 属性(static) 优先于实例的方法 / 属性(instance);属性(field) 优先于构造函数(constructor), 优先于方法(method);公开的成员(public) 优先于受保护的成员(protected), 优先于私有的成员(private);
- 【推荐】如果类的属性是一个字面量, 则推荐使用只读属性 readonly 而不是 getter。
类上所有返回「字面量」的 getter 方法, 都推荐使用 readonly 修饰符来代替, 包括字符串 、数字等。
2.4.3. 接口
-
【强制】优先使用 Interface 编写。
-
【推荐】接口中的方法使用属性的方式定义。
-
【推荐】避免定义空的接口类型。
-
【强制】interface 和 type 定义时必须声明成员的类型。
2.4.4. 模块
- 【推荐】使用 ES2015 import 语法引入模块。
2.4.5. 重载
- 【强制】重载函数写在一起以提高可读性。
2.4.6. 声明
- 【推荐】简单数组类型的定义使用 T[], 复杂类型使用 Array。
简单类型(数字、字符串、布尔等)请使用 T[] 或 readonly T[] ,其他复杂类型(联合 、交叉 、对象 、函数等)请使用 Array 或 ReadonlyArray。
- 【推荐】初始化为 number/string/boolean 的变量或参数应避免显式的类型声明。
Ts 会主动帮我们推导出类型, 显式声明会导致代码冗余。
- 【强制】禁止无意义的 void 类型。
void 类型代表「无」或函数「不返回任何值」, 隐式未定义类型代表函数返回「未定义的值 undefined」, 所以 void 类型无法与除了 never 外的其他类型做联合 、交叉。
2.4.7. 注释
- 【推荐】使用 TypeScript 注释指令时需跟随描述说明。
2.4.8. 断言
- 【推荐】禁止使用容易混淆的非空断言。
在相等运算符之前增加非空断言容易与不等于混淆, 所以不建议使用。
- 【强制】类型断言必须使用 as Type。
2.4.9. 命名空间
- 【强制】禁止使用 namespace 来定义命名空间。
自定义 TypeScript 模块(module) 和命名空间(namespace) 已经不再推荐使用,首选 ES2015 的模块语法来导入导出。此规则仍然允许定义外部的模块或命名空间。
2.4.10. 变量
-
【强制】不得使用 var 声明变量。
-
【推荐】只使用const 声明变量,在 const 无法有效涵盖的场景,可按需使用let。
-
【推荐】如非必要,不能使用 any 类型标注,使用 any 将会失去 Typescript的类型检查功能 。一般而言, 仅限于与第三方 Javascript 库联合使用, 且无对应定义时可使用 any。
-
【参考】一般而言,建议使用 undefined,如非必要,不要使用 null;对于 Vue组件成员初始值,若使用 undefined 可能会导致响应式故障, 可使用初始值代替 。如初始值无意义, 可使用 null。
2.4.11. 枚举
- 【参考】使用联合类型替代枚举, 如非必要, 避免在新代码中使用枚举。