目录
命名规则
项目文件命名
全部采用小写方式, 以下划线分隔。
例:my_project_name
目录命名
参照项目命名规则。
有复数结构时,要采用复数命名法。
例:scripts, styles, images, data_models
JS 文件命名
参照项目命名规则。
例:account_model.js
CSS, SCSS 文件命名
参照项目命名规则。
例:retina_sprites.scss
HTML 文件命名
参照项目命名规则。
例:error_report.html
VUE 组件命名
组件名首字母大写驼峰,应该由多单词组成,除了一些内置组件,如果是单个单词,加上标识 V
例:
ToDoItem.vue
、VTable.vue
、VDetail.vue
缩进
HTML、JavaScript、CSS、SCSS、Vue,这些文件的内容全部使用2个空格缩进。可对IDE进行统一设置。
HTML 代码规范
- 嵌套的节点按照结构,层层缩进,同时要注意 HTML的语义化;
- 在属性上,使用双引号,不要使用单引号;
- 自定义属性名全小写,用中划线做分隔符;
- 当单个元素的属性过多时,需要适当换行,方便查看。
CSS 代码规范
空格规范
以下几种情况不需要空格:
- 属性名后
- 多个规则的分隔符','前
!important
'!'后- 属性值中'('后和')'前
- 行末不要有多余的空格
以下几种情况需要空格:
- 属性值前
- 选择器'>', '+', '~'前后
- '{'前
!important
'!'前@else
前后- 属性值中的','后
- 注释'/*'后和'*/'前
/* not good */
.element {
color :red! important;
background-color: rgba(0,0,0,.5);
}
/* good */
.element {
color: red !important;
background-color: rgba(0, 0, 0, .5);
}
/* not good */
.element ,
.dialog{
...
}
/* good */
.element,
.dialog {
}
/* not good */
.element>.dialog{
...
}
/* good */
.element > .dialog{
...
}
/* not good */
.element{
...
}
/* good */
.element {
...
}
空行/换行
以下几种情况需要空行:
- 文件最后保留一个空行
- '}'后最好跟一个空行,包括scss中嵌套的规则
以下几种情况不需要换行:
- '{'前
以下几种情况需要换行:
- '{'后和'}'前
- 每个属性独占一行
- 多个规则的分隔符','后
/* 空行 */
/* not good */
.element {
...
}
.dialog {
color: red;
&:after {
...
}
}
/* good */
.element {
...
}
.dialog {
color: red;
&:after {
...
}
}
/* 换行 */
/* not good */
.element
{color: red; background-color: black;}
/* good */
.element {
color: red;
background-color: black;
}
/* not good */
.element, .dialog {
...
}
/* good */
.element,
.dialog {
...
}
引号
最外层统一使用双引号;
url的内容要用引号;
属性选择器中的属性值需要引号。
.element:after {
content: "";
background-image: url("logo.png");
}
li[data-type="single"] {
...
}
命名
- 类名使用小写字母,以中划线分隔
- id采用驼峰式命名
- scss中的变量、函数、混合、placeholder采用驼峰式命名
-
css命名规范也可以学习参考BEM策略;
/* class */
.element-content {
...
}
/* id */
#myDialog {
...
}
/* 变量 */
$colorBlack: #000;
/* 函数 */
@function pxToRem($px) {
...
}
/* 混合 */
@mixin centerBlock {
...
}
/* placeholder */
%myDialog {
...
}
属性声明顺序
相关的属性声明按顺序做分组处理,组之间需要有一个空行。
.declaration-order {
display: block;
float: right;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
border: 1px solid #e5e5e5;
border-radius: 3px;
width: 100px;
height: 100px;
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
text-align: center;
color: #333;
background-color: #f5f5f5;
opacity: 1;
}
// 下面是推荐的属性的顺序
[
[
"display",
"visibility",
"float",
"clear",
"overflow",
"overflow-x",
"overflow-y",
"clip",
"zoom"
],
[
"table-layout",
"empty-cells",
"caption-side",
"border-spacing",
"border-collapse",
"list-style",
"list-style-position",
"list-style-type",
"list-style-image"
],
[
"-webkit-box-orient",
"-webkit-box-direction",
"-webkit-box-decoration-break",
"-webkit-box-pack",
"-webkit-box-align",
"-webkit-box-flex"
],
[
"position",
"top",
"right",
"bottom",
"left",
"z-index"
],
[
"margin",
"margin-top",
"margin-right",
"margin-bottom",
"margin-left",
"-webkit-box-sizing",
"-moz-box-sizing",
"box-sizing",
"border",
"border-width",
"border-style",
"border-color",
"border-top",
"border-top-width",
"border-top-style",
"border-top-color",
"border-right",
"border-right-width",
"border-right-style",
"border-right-color",
"border-bottom",
"border-bottom-width",
"border-bottom-style",
"border-bottom-color",
"border-left",
"border-left-width",
"border-left-style",
"border-left-color",
"-webkit-border-radius",
"-moz-border-radius",
"border-radius",
"-webkit-border-top-left-radius",
"-moz-border-radius-topleft",
"border-top-left-radius",
"-webkit-border-top-right-radius",
"-moz-border-radius-topright",
"border-top-right-radius",
"-webkit-border-bottom-right-radius",
"-moz-border-radius-bottomright",
"border-bottom-right-radius",
"-webkit-border-bottom-left-radius",
"-moz-border-radius-bottomleft",
"border-bottom-left-radius",
"-webkit-border-image",
"-moz-border-image",
"-o-border-image",
"border-image",
"-webkit-border-image-source",
"-moz-border-image-source",
"-o-border-image-source",
"border-image-source",
"-webkit-border-image-slice",
"-moz-border-image-slice",
"-o-border-image-slice",
"border-image-slice",
"-webkit-border-image-width",
"-moz-border-image-width",
"-o-border-image-width",
"border-image-width",
"-webkit-border-image-outset",
"-moz-border-image-outset",
"-o-border-image-outset",
"border-image-outset",
"-webkit-border-image-repeat",
"-moz-border-image-repeat",
"-o-border-image-repeat",
"border-image-repeat",
"padding",
"padding-top",
"padding-right",
"padding-bottom",
"padding-left",
"width",
"min-width",
"max-width",
"height",
"min-height",
"max-height"
],
[
"font",
"font-family",
"font-size",
"font-weight",
"font-style",
"font-variant",
"font-size-adjust",
"font-stretch",
"font-effect",
"font-emphasize",
"font-emphasize-position",
"font-emphasize-style",
"font-smooth",
"line-height",
"text-align",
"-webkit-text-align-last",
"-moz-text-align-last",
"-ms-text-align-last",
"text-align-last",
"vertical-align",
"white-space",
"text-decoration",
"text-emphasis",
"text-emphasis-color",
"text-emphasis-style",
"text-emphasis-position",
"text-indent",
"-ms-text-justify",
"text-justify",
"letter-spacing",
"word-spacing",
"-ms-writing-mode",
"text-outline",
"text-transform",
"text-wrap",
"-ms-text-overflow",
"text-overflow",
"text-overflow-ellipsis",
"text-overflow-mode",
"-ms-word-wrap",
"word-wrap",
"-ms-word-break",
"word-break"
],
[
"color",
"background",
"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader",
"background-color",
"background-image",
"background-repeat",
"background-attachment",
"background-position",
"-ms-background-position-x",
"background-position-x",
"-ms-background-position-y",
"background-position-y",
"-webkit-background-clip",
"-moz-background-clip",
"background-clip",
"background-origin",
"-webkit-background-size",
"-moz-background-size",
"-o-background-size",
"background-size"
],
[
"outline",
"outline-width",
"outline-style",
"outline-color",
"outline-offset",
"opacity",
"filter:progid:DXImageTransform.Microsoft.Alpha(Opacity",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
"-ms-interpolation-mode",
"-webkit-box-shadow",
"-moz-box-shadow",
"box-shadow",
"filter:progid:DXImageTransform.Microsoft.gradient",
"-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
"text-shadow"
],
[
"-webkit-transition",
"-moz-transition",
"-ms-transition",
"-o-transition",
"transition",
"-webkit-transition-delay",
"-moz-transition-delay",
"-ms-transition-delay",
"-o-transition-delay",
"transition-delay",
"-webkit-transition-timing-function",
"-moz-transition-timing-function",
"-ms-transition-timing-function",
"-o-transition-timing-function",
"transition-timing-function",
"-webkit-transition-duration",
"-moz-transition-duration",
"-ms-transition-duration",
"-o-transition-duration",
"transition-duration",
"-webkit-transition-property",
"-moz-transition-property",
"-ms-transition-property",
"-o-transition-property",
"transition-property",
"-webkit-transform",
"-moz-transform",
"-ms-transform",
"-o-transform",
"transform",
"-webkit-transform-origin",
"-moz-transform-origin",
"-ms-transform-origin",
"-o-transform-origin",
"transform-origin",
"-webkit-animation",
"-moz-animation",
"-ms-animation",
"-o-animation",
"animation",
"-webkit-animation-name",
"-moz-animation-name",
"-ms-animation-name",
"-o-animation-name",
"animation-name",
"-webkit-animation-duration",
"-moz-animation-duration",
"-ms-animation-duration",
"-o-animation-duration",
"animation-duration",
"-webkit-animation-play-state",
"-moz-animation-play-state",
"-ms-animation-play-state",
"-o-animation-play-state",
"animation-play-state",
"-webkit-animation-timing-function",
"-moz-animation-timing-function",
"-ms-animation-timing-function",
"-o-animation-timing-function",
"animation-timing-function",
"-webkit-animation-delay",
"-moz-animation-delay",
"-ms-animation-delay",
"-o-animation-delay",
"animation-delay",
"-webkit-animation-iteration-count",
"-moz-animation-iteration-count",
"-ms-animation-iteration-count",
"-o-animation-iteration-count",
"animation-iteration-count",
"-webkit-animation-direction",
"-moz-animation-direction",
"-ms-animation-direction",
"-o-animation-direction",
"animation-direction"
],
[
"content",
"quotes",
"counter-reset",
"counter-increment",
"resize",
"cursor",
"-webkit-user-select",
"-moz-user-select",
"-ms-user-select",
"user-select",
"nav-index",
"nav-up",
"nav-right",
"nav-down",
"nav-left",
"-moz-tab-size",
"-o-tab-size",
"tab-size",
"-webkit-hyphens",
"-moz-hyphens",
"hyphens",
"pointer-events"
]
]
颜色
颜色16进制用小写字母;
JavaScript 代码规范
分号
为了使代码统一且整洁,所有行行末非必要不使用分号,以下情况需要使用分号:
以 “[”、“(”、“+”、“-”开头的行,需要在行首使用分号;
//反例 会报错
let hey = 'hey'
['hello', 'world'].forEach(...)
//正确
let hey = 'hey'
;['hello', 'world'].forEach(...)
//反例 会报错
const a = 1
const b = 2
const c = a + b
(a + b).toString()
//正确
const a = 1
const b = 2
const c = a + b
;(a + b).toString()
空格
以下几种情况不需要空格:
- 对象的属性名后
- 前缀一元运算符后
- 后缀一元运算符前
- 函数调用括号前
- 无论是函数声明还是函数表达式,'('前不要空格
- 数组的'['后和']'前
- 对象的'{'后和'}'前
- 运算符'('后和')'前
以下几种情况需要空格:
- 二元运算符前后
- 三元运算符'?:'前后
- 代码块'{'前
- 下列关键字前:
else
,while
,catch
,finally
- 下列关键字后:
if
,else
,for
,while
,do
,switch
,case
,try
,catch
,finally
,with
,return
,typeof
- 单行注释'//'后(若单行注释和代码同行,则'//'前也需要),多行注释'*'后
- 对象的属性值前
- for循环,分号后留有一个空格,前置条件如果有多个,逗号后留一个空格
- 无论是函数声明还是函数表达式,'{'前一定要有空格
- 函数的参数之间
// not good
let a = {
b :1
}
// good
let a = {
b: 1
}
// not good
++ x
y ++
z = x?1:2
// good
++x
y++
z = x ? 1 : 2
// not good
let a = [ 1, 2 ]
// good
let a = [1, 2]
// not good
let a = ( 1+2 )*3
// good
let a = (1 + 2) * 3
// no space before '(', one space before '{', one space between function parameters
let doSomething = function(a, b, c) {
// do something
}
// no space before '('
doSomething(item)
// not good
for(i=0;i<6;i++){
x++
}
// good
for (i = 0; i < 6; i++) {
x++
}
换行
换行的地方,行末必须有','或者运算符;
以下几种情况不需要换行:
- 下列关键字后:
else
,catch
,finally
- 代码块'{'前
以下几种情况需要换行:
- 代码块'{'后和'}'前
- 变量赋值后
// not good
let a = {
b: 1
, c: 2
}
// good
let a = {
b: 1,
c: 2
}
// no need line break with 'else', 'catch', 'finally'
if (condition) {
...
} else {
...
}
try {
...
} catch (e) {
...
} finally {
...
}
// not good
function test()
{
...
}
// good
function test() {
...
}
引号
最外层统一使用单引号。
// not good
let x = "test"
// good
let y = 'foo',
z = '<div id="test"></div>'
变量命名
- 标准变量采用驼峰式命名;
- 变量名首选用有意义的单词,非必要不使用单字母、中文和拼音,使用拼音时用全拼字母;
- 常量全大写,用下划线连接;
变量声明
优先使用const和let声明变量,非必要不使用var。
函数
- 函数命名规范同变量命名;
- 无论是函数声明还是函数表达式,'('前不要空格,但'{'前一定要有空格;
- 函数调用括号前不需要空格;
- 参数之间用', '分隔,注意逗号后有一个空格。
- 使用有意义的前缀名称,例如:
- get、find等代表函数返回一个值;
- list、getXxxList、getXxxArray等代表返回一个集合列表;
- add、insert代表函数将用于新增;
- update、edit、set代表函数用于更新;
- delete、remove代表函数用于删除;
- on、handle用于事件处理函数;
- is、should、can代表函数返回一个布尔类型;
// no space before '(', but one space before'{'
let doSomething = function(item) {
// do something
}
function doSomething(item) {
// do something
}
// not good
doSomething (item);
// good
doSomething(item);
数组、对象
- 对象属性名不需要加引号;
- 对象以缩进的形式书写,不要写在一行;
- 数组、对象最后不要有逗号。
// not good
let a = {
'b': 1
}
let a = {b: 1};
let a = {
b: 1,
c: 2,
}
// good
let a = {
b: 1,
c: 2
}
括号
下列关键字后必须有大括号(即使代码块的内容只有一行):if
, else
, for
, while
, do
, switch
, try
, catch
, finally
, with
。
// not good
if (condition)
doSomething()
// good
if (condition) {
doSomething()
}
undefined
永远不要直接使用undefined进行变量判断;
使用typeof和字符串'undefined'对变量进行判断。
// not good
if (person === undefined) {
...
}
// good
if (typeof person === 'undefined') {
...
}
其他
- 用'===', '!=='代替'==', '!=';
- 内容过长或者逻辑复杂的函数,尽量多写注释并将内部拆分成多个函数,使逻辑清晰;
- 重复使用的代码或者后续能使用到的部分,尽量封装成函数;
Vue 风格指南
优先级A的规则:必要的(规避错误)
组件名为多个单词
组件名应该始终是多个单词的,根组件 App 以及 <transition>、<component> 之类的 Vue 内置组件除外。这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
//反例
Vue.component('todo', {
// ...
})
export default {
name: 'Todo',
// ...
}
//好例子
Vue.component('todo-item', {
// ...
})
export default {
name: 'TodoItem',
// ...
}
Prop 定义
在你提交的代码中,prop 的定义应该尽量详细,至少需要指定其类型。
//反例
props: ['status']
//好例子
props: {
status: String
}
//更好的例子
props: {
status: {
type: String,
required: true,
validator: function (value) {
return [
'syncing',
'synced',
'version-conflict',
'error'
].indexOf(value) !== -1
}
}
}
为 v-for 设置键值
在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。
//反例
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
//好例子
<ul>
<li
v-for="todo in todos"
:key="todo.id"
>
{{ todo.text }}
</li>
</ul>
避免 v-if 和 v-for 用在一起
一般我们在两种常见的情况下会倾向于这样做:
- 为了过滤一个列表中的项目 (比如 v-for="user in users" v-if="user.isActive")。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。
- 为了避免渲染本应该被隐藏的列表 (比如 v-for="user in users" v-if="shouldShowUsers")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul、ol)。
//反例
<ul>
<li
v-for="user in users"
v-if="user.isActive"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
//反例
<ul>
<li
v-for="user in users"
v-if="shouldShowUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
//好例子
<ul>
<li
v-for="user in activeUsers"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
//好例子
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
优先级B:强烈推荐
优先级C:推荐
优先级D:谨慎使用
优先级B/C/D,可参考Vue官方风格指南