概述
本文档结合了AirBnb代码规范及阿里代码规范制订而成。
通用
公共部分
- 缩进:前端代码应当采用空格而非 tab 进行缩进,每次缩进为2个空格。
- 行末空格:通常应当删除行末多余空格。md 文件比较特殊,行末4 个空格可作为换行标志,因此可以保留。
- 文末空行:文件末尾应当保留一个空行,原因是某些 GNU 软件默认文件最后一行是空行,进行文件处理时会忽略最后一行。
- 文件名不得含有空格。
项目命名
全部采用小写方式, 以短横线分隔。例:my-project-name。
目录命名
参照项目命名规则,有复数结构时,要采用复数命名法,缩写不用复数。
例:docs、assets、components、directives、mixins、utils、views等。
例:scripts/styles/components/images/utils/layouts/demo-styles/demo-scripts/img/doc
文件命名
文件名称统一用小写的英文字母、数字和下划线的组合,其中不得包含汉字、空格和特殊字符。
命名原则的指导思想是使得你自己和工作组的每一个成员能够方便的理解每一个文件的意义。尽可能地使用标准的英文翻译。不能出现拼写错误的情况。代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。注意,即使纯拼音命名方式也要避免采用。杜绝完全不规范的缩写,避免望文不知义。
JS、CSS、SCSS、HTML、PNG 等文件:全部采用小写方式, 优先选择单个单词命名,多个单词命名以短横线分隔。如:
date-picker.scss
、normalize.less
等。
特殊的文件命名
Vue.js
单文件组件
文件扩展名为 .vue 的 single-file components。单文件组件名应该始终是单词大写开头 (PascalCase)。组件名应该倾向于而不是缩写。 编辑器中的自动补全已经让书写长命名的代价非常低了,而其带来的明确性却是非常宝贵的。不常用的缩写尤其应该避免。组件名应该始终是多个单词组成(大于等于 2)。这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。如:MyComponent.vue
、StudentDashboardSettings.vue
、UserProfileOptions.vue
等。
单例组件
只拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。
这不意味着组件只可用于一个单页面,而是_每个页面_只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,_只是目前_在每个页面里只使用一次。
比如,头部和侧边栏组件几乎在每个页面都会使用,不接受 prop,该组件是专门为该应用所定制的。如:TheHeader.vue
、TheFooter.vue
等。
基础组件
不包含业务,独立、具体功能的基础组件,比如日期选择器、模态框等,这类组件作为项目的基础控件,会被大量使用,因此组件的 API 进行过高强度的抽象,可以通过不同配置实现不同的功能。
应用特定样式和约定的基础组件(也就是展示类的、无逻辑的或无状态、不掺杂业务逻辑的组件) 应该全部以一个特定的前缀开头 —— Base。基础组件在一个页面内可使用多次,在不同页面内也可复用,是高可复用组件。如:BaseButton.vue
、BaseTable.vue
等。
业务组件
业务组件:它不像基础组件只包含某个功能,而是在业务中被多个页面复用的(具有可复用性),它与基础组件的区别是,业务组件只在当前项目中会用到,不具有通用性,而且会包含一些业务,比如数据请求;而基础组件不含业务,在任何项目中都可以使用,功能单一,比如一个具有数据校验功能的输入框。
掺杂了复杂业务的组件(拥有自身 data、prop 的相关处理)即业务组件应该以 Custom 前缀命名。业务组件在一个页面内比如:某个页面内有一个卡片列表,而样式和逻辑跟业务紧密相关的卡片就是业务组件。如:CustomCard.vue
、CustomVisualizationTable.vue
等。
紧密耦合的组件
和父组件紧密耦合的子组件应该以父组件名作为前缀命名。 因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。如:TodoList.vue
、TodoListItem.vue
、TodoListItemButton.vue
等。
组件名中单词顺序
组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。 因为编辑器通常会按字母顺序组织文件,所以现在组件之间的重要关系一目了然。如下组件主要是用于搜索和设置功能。
如:SearchButtonClear.vue
、SearchButtonRun.vue
、SearchInputQuery.vue
、SearchInputExcludeGlob.vue
等。
注释
遵循 JSDoc 标准注释
页面文件头部:包括描述、作者、开发时间、使用的接口、接收到页面参数等,如:
1
2
3
4
5
6
7/**
* 商品详情页面
* @author myd 2022-11-14
* @interface MAL313-商品详情接口
* @interface MAL301-收藏接口
* @param {String} productId - 商品 ID
*/变量、常量都需要注释:包括类型、说明、枚举值等
1
let certType; //{Number}证件类型,1-身份证 2-驾驶证
函数都需要注释:其中公用函数的注释应尽量详细,应包含描述、参数、返回、示例等
调用接口的参数:都需要注释,包含说明、枚举值等
待定事项使用@todo或// TODO
1
2
3
4
5color: #fa0; //@todo 待UI确认
function getProductDetail() {
// TODO 待后端给出接口
}
HTML(含Vue Template)
缩进:缩进使用 2 个空格(一个 tab),嵌套的节点应该缩进。
分块注释:在每一个块状元素,列表元素和表格元素后,加上一对 HTML 注释。重要节点要补充注释。
语义化标签:HTML5 中新增很多语义化标签,所以优先使用语义化标签,避免一个页面都是 div 或者 p 标签。
引号:使用双引号(“ “) 而不是单引号(’ ‘) 。
正例:<div class="box"></div>
反例:<div class='box'></div>
布尔值属性不设置值,如:
1
2
3
4<select>
<option value="1" selected>选项一</option>
</select>
<button disabled>禁用的按钮</button>结构顺序和视觉顺序基本保持一致:按照从上至下、从左到右的视觉顺序书写HTML结构;有时候为了便于搜索引擎抓取,我们也会将重要内容在HTML 结构顺序上提前。
结构、表现、行为三者分离,避免内联:内联样式会增加渲染成本且难以维护。除非模板中样式需要 js 逻辑去控制,如通过 vue 的计算属性。
避免使用元素 id:一般元素 id 只适合应用在框架中,如左边菜单树、头部导航栏。
JavaScript
通用
命名
采用小写驼峰命名 lowerCamelCase,代码中的命名均不能含有下划线, 也不能以下划线或美元符号结束。
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
正例:
localValue / getHttpMessage() / inputUserId
method 方法命名必须是 动词 或者 动词+名词 形式
正例: saveShopCarData /openShopCarInfoDialog
反例: save / open / show / go
增删查改,详情统一使用如下 5 个单词,不得使用其他(目的是为了统一各个端):
add / update / delete / detail / get
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚, 不要嫌名字长
正例:MAX_STOCK_COUNT
反例:
MAX_COUNT
引用
尽可能地使用 const
来进行赋值,避免使用 var
。
如果一定要对参数重新赋值,使用 let
,而不是 var
。
字符串
统一使用单引号(‘),不使用双引号(“)。这在创建 HTML 字符串非常有好处。
正例:
let str = 'foo';
let testDiv = '<div id="test"></div>';
对象
使用字面值创建对象
正例:let user = {};
反例:
let user = new Object();
使用字面量来代替对象构造器
正例:let user = { age: 0, name: 1, city: 3 };
反例:
1
2
3
4var user = new Object();
user.age = 0;
user.name = 0;
user.city = 0;
使用 ES6+
必须优先使用 ES6+ 中新增的语法糖和函数。这将简化你的程序,并让你的代码更加灵活和可复 用。比如箭头函数、await/async , 解构, let , for…of 等等。
这是常见的各种ES6特性:
- 箭头函数——Arrow Functions
- 类——Classes
- 对象缩写——Object Shorthand
- 对象简写——Object Concise
- 对象计算属性——Object Computed Properties
- 模板字符串——Template Strings
- 解构赋值——Destructuring
- 默认参数——Default Parameters
- 剩余参数——Rest
- 数组拓展——Array Spreads
- Let and Const
- 幂操作符——Exponentiation Operator
- 迭代器和生成器——Iterators and Generators
- 模块——Modules
括号
下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try,catch, finally, with。
undefined 判断
永远不要直接使用 undefined 进行变量判断;使用 typeof 和字符串’undefined’对变量进行判断。
正例:if (typeof person === 'undefined') { ... }
反例:if (person === undefined) { ... }
条件判断和循环最多三层
条件判断能使用三目运算符和逻辑运算符解决的,就不要使用条件判断,但是谨记不要写太长的 三目运算符。如果超过 3 层请抽成函数,并写清楚注释。
this 的转换命名
对上下文 this 的引用只能使用 self
或_this
来命名。
慎用 console.log
因 console.log 大量使用会有性能问题,所以在非 webpack 项目中谨慎使用 log 功能。
尽可能使用强等于(不等于)代替弱等于(不等于)
绝对不要使用 eval!永远不要使用 Function 构造函数!永远不要将字符串传递给setTimeout 或 setInterval!
Vue.js 2.x
规范基础
Vue 项目规范以 Vue 官方规范 ( https://v2.cn.vuejs.org/v2/style-guide/) 中的 A 规范为基础,在其上面进行项目开发,故所有代码均遵守该规范。
在 Template 模版中使用组件
应使用 PascalCase 模式,并且使用自闭合组件。
正例:
1 | <!-- 在单文件组件、字符串模板和 JSX 中 --> |
反例:
1 | <my-component /> |
组件的 data 必须是一个函数
当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。因为如果直接是一个对象的话,子组件之间的属性值会互相影响。
Prop 定义应该尽量详细
必须使用 camelCase 驼峰命名
必须指定类型
必须加上注释,表明其含义
必须加上 required 或者 default,两者二选其一
如果有业务需要,必须加上 validator 验证
为组件样式设置作用域
正例:
1 | <template> |
反例:
1 | <template> |
模板中使用简单的表达式
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
正例:
1 | <template> |
反例:
1 | <template> |
指令都使用缩写形式
正例:
1 | <input @input="onInput" @focus="onFocus" /> |
反例:
1 | <input v-on:input="onInput" @focus="onFocus" /> |
标签顺序保持一致
单文件组件应该总是让标签顺序保持为template > script > style
正例:
1 | <template>...</template> |
反例:
1 | <template>...</template> |
必须为 v-for 设置键值 key
v-show 与 v-if 选择
如果运行时,需要非常频繁地切换,使用 v-show ;如果在运行时,条件很少改变,使用 v-if。
script 标签内部结构顺序
components > props > data > computed > watch > filter > 钩子函数(钩子函数按其执行顺序) > methods
uni-app
使用template元素而非block元素
uni-app
支持在 template 模板中嵌套 template
和 block
,用来进行 列表渲染 和 条件渲染。
template
和 block
并不是一个组件,它们仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性。
block
在不同的平台表现存在一定差异,推荐统一使用 template
。
代码示例
1 | <template> |
1 | <template> |
兼容app-nvue平台时使用flex布局
CSS
术语
规则声明
我们把一个(或一组)选择器和一组属性称之为 “规则声明”。举个例子:
1 | .listing { |
选择器
在规则声明中,“选择器” 负责选取 DOM 树中的元素,这些元素将被定义的属性所修饰。选择器可以匹配 HTML 元素,也可以匹配一个元素的类名、ID, 或者元素拥有的属性。以下是选择器的例子:
1 | .my-element-class { |
属性
最后,属性决定了规则声明里被选择的元素将得到何种样式。属性以键值对形式存在,一个规则声明可以包含一或多个属性定义。以下是属性定义的例子:
1 | /* some selector */ { |
通用
尽可能将所有 css 写在外部样式表中。
不要在外部样式表里面利用@import 导入其他 css 文件。
尽量避免使用 css 表达式。
选择器的顺序应与元素保持一致。
属性声明的顺序:遵循先布局后样式,特殊地,伪类元素的content 应放于最前面。
参考顺序:定位→布局→盒子→尺寸→文本排版→视觉外观。
定位:position(包括 top、right、bottom、left、z-index)、float、clear 等
布局:display(包括 vertical-align、弹性布局的 flex-direction、align-items 等)、overflow等
盒子:box-sizing、margin、padding、border 等
尺寸:width、height、line-height 等
文本排版:font、text-align、white-space 等
视觉外观:transform、color、border-radius、transition、background等
格式
- 使用 2 个空格作为缩进。
- 类名建议使用破折号代替驼峰法。如果你使用 BEM,也可以使用下划线(参见下面的 OOCSS 和 BEM)。
- 不要使用 ID 选择器。
- 在一个规则声明中应用了多个选择器时,每个选择器独占一行。
- 在规则声明的左大括号
{
前加上一个空格。 - 在属性的冒号
:
后面加上一个空格,前面不加空格。 - 规则声明的右大括号
}
独占一行。 - 规则声明之间用空行分隔开。
Bad
1 | .avatar{ |
Good
1 | .avatar { |
注释
- 建议使用行注释 (在 Sass 中是
//
) 代替块注释。 - 建议注释独占一行。避免行末注释。
- 给没有自注释的代码写上详细说明,比如:
- 为什么用到了 z-index
- 兼容性处理或者针对特定浏览器的 hack
OOCSS 和 BEM
出于以下原因,我们鼓励使用 OOCSS 和 BEM 的某种组合:
- 可以帮助我们理清 CSS 和 HTML 之间清晰且严谨的关系。
- 可以帮助我们创建出可重用、易装配的组件。
- 可以减少嵌套,降低特定性。
- 可以帮助我们创建出可扩展的样式表。
OOCSS,也就是 “Object Oriented CSS(面向对象的CSS)”,是一种写 CSS 的方法,其思想就是鼓励你把样式表看作“对象”的集合:创建可重用性、可重复性的代码段让你可以在整个网站中多次使用。
参考资料:
- Nicole Sullivan 的 OOCSS wiki
- Smashing Magazine 的 Introduction to OOCSS
BEM,也就是 “Block-Element-Modifier”,是一种用于 HTML 和 CSS 类名的_命名约定_。BEM 最初是由 Yandex 提出的,要知道他们拥有巨大的代码库和可伸缩性,BEM 就是为此而生的,并且可以作为一套遵循 OOCSS 的参考指导规范。
- CSS Trick 的 BEM 101
- Harry Roberts 的 introduction to BEM
示例
1 | <article class="listing-card listing-card--featured"> |
1 | .listing-card { } |
.listing-card
是一个块(block),表示高层次的组件。.listing-card__title
是一个元素(element),它属于.listing-card
的一部分,因此块是由元素组成的。.listing-card--featured
是一个修饰符(modifier),表示这个块与.listing-card
有着不同的状态或者变化。
ID 选择器
在 CSS 中,虽然可以通过 ID 选择元素,但大家通常都会把这种方式列为反面教材。ID 选择器给你的规则声明带来了不必要的高优先级,而且 ID 选择器是不可重用的。
想要了解关于这个主题的更多内容,参见 CSS Wizardry 的文章,文章中有关于如何处理优先级的内容。
JavaScript 钩子
避免在 CSS 和 JavaScript 中绑定相同的类。否则开发者在重构时通常会出现以下情况:轻则浪费时间在对照查找每个要改变的类,重则因为害怕破坏功能而不敢作出更改。
我们推荐在创建用于特定 JavaScript 的类名时,添加 .js-
前缀:
1 | <button class="btn btn-primary js-request-to-book">Request to Book</button> |
边框
在定义无边框样式时,使用 0
代替 none
。
Bad
1 | .foo { |
Good
1 | .foo { |
省略0后面的单位
Bad
1 | .foo { |
Good
1 | .foo { |
尽量使用缩写属性
Bad
1 | .foo { |
Good
1 | .foo { |
使用CSS预处理器(Scss/Sass/Less等)
通用
文件组织
文件按以下顺序组织:
- @import
- 变量声明
- 样式声明
避免嵌套层级过多
请不要让嵌套选择器的深度超过 3 层!
1 | .page-container { |
对于超过 4 级的嵌套,给予重新评估。这可以避免出现过于详实的 CSS 选择器。避免大量的嵌套规则。当可读性受到影响时,将之打断。推荐避免出现多于 20 行的嵌套规则出现。
当遇到以上情况的时候,你也许是这样写 CSS 的:
- 与 HTML 强耦合的(也是脆弱的)—或者—
- 过于具体(强大)—或者—
- 没有重用
再说一遍: 永远不要嵌套 ID 选择器!
如果你始终坚持要使用 ID 选择器(劝你三思),那也不应该嵌套它们。如果你正打算这么做,你需要先重新检查你的标签,或者指明原因。如果你想要写出风格良好的 HTML 和 CSS,你是不应该这样做的。
Scss/Sass
语法
- 使用
.scss
的语法,不使用.sass
原本的语法。 - CSS 和
@include
声明按照以下逻辑排序(参见下文)
属性声明的排序
属性声明
首先列出除去
@include
和嵌套选择器之外的所有属性声明。1
2
3
4
5.btn-green {
background: green;
font-weight: bold;
// ...
}@include
声明紧随后面的是
@include
,这样可以使得整个选择器的可读性更高。1
2
3
4
5
6.btn-green {
background: green;
font-weight: bold;
@include transition(background 0.5s ease);
// ...
}嵌套选择器
_如果有必要_用到嵌套选择器,把它们放到最后,在规则声明和嵌套选择器之间要加上空白,相邻嵌套选择器之间也要加上空白。嵌套选择器中的内容也要遵循上述指引。
1
2
3
4
5
6
7
8
9.btn {
background: green;
font-weight: bold;
@include transition(background 0.5s ease);
.icon {
margin-right: 10px;
}
}
变量
变量名应使用破折号(例如 $my-variable
)代替 camelCased 和 snake_cased 风格。对于仅用在当前文件的变量,可以在变量名之前添加下划线前缀(例如 $_my-variable
)。
Mixins
为了让代码遵循 DRY 原则(Don’t Repeat Yourself)、增强清晰性或抽象化复杂性,应该使用 mixin,这与那些命名良好的函数的作用是异曲同工的。虽然 mixin 可以不接收参数,但要注意,假如你不压缩负载(比如通过 gzip),这样会导致最终的样式包含不必要的代码重复。
扩展指令
应避免使用 @extend
指令,因为它并不直观,而且具有潜在风险,特别是用在嵌套选择器的时候。即便是在顶层占位符选择器使用扩展,如果选择器的顺序最终会改变,也可能会导致问题。(比如,如果它们存在于其他文件,而加载顺序发生了变化)。其实,使用 @extend 所获得的大部分优化效果,gzip 压缩已经帮助你做到了,因此你只需要通过 mixin 让样式表更符合 DRY 原则就足够了。
Less
同Scss(除Scss特殊指令名外)
附录
函数方法常用的动词:
1 | get 获取/set 设置, |