# 前端规范
# CSS编码规范
# 1 前言
CSS 作为网页样式的描述语言,一直有着广泛的应用。本文档的目标是使 CSS 代码风格保 持一致,容易被理解和被维护。
虽然本文档是针对 CSS 设计的,但是在使用各种 CSS 的预编译器(如 less、sass、stylus 等)时,适用的部分也应尽量遵循本文档的约定。
# 2 代码风格
# 2.1 文件
- [建议]
CSS
文件使用无BOM
的UTF-8
编码。
解释:
UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰 。
# 2.2 缩进
- [强制] 使用
4
个空格做为一个缩进层级,不允许使用2
个空格 或tab
字符。
示例:
.selector {
margin: 0;
padding: 0;
}
# 2.3 空格
- [强制]
选择器
与{
之间必须包含空格。
示例:
.selector {
}
- [强制]
属性名
与之后的:
之间不允许包含空格,:
与属性值
之间必须包含空格。
示例:
margin: 0;
- [强制]
列表型属性值
书写在单行时,,
后必须跟一个空格。
示例:
font-family: Arial, sans-serif;
# 2.4 行长度
- [强制] 每行不得超过
120
个字符,除非单行不可分割。
解释:
常见不可分割的场景为 URL 超长。
- [建议] 对于超长的样式,在样式值的
空格
处或,
后换行,建议按逻辑分组。
示例:
/* 不同属性值按逻辑分组 */
background: transparent url(aVeryVeryVeryLongUrlIsPlacedHere) no-repeat 0 0;
/* 可重复多次的属性,每次重复一行 */
background-image: url(aVeryVeryVeryLongUrlIsPlacedHere)
url(anotherVeryVeryVeryLongUrlIsPlacedHere);
/* 类似函数的属性值可以根据函数调用的缩进进行 */
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.04, rgb(88, 94, 124)),
color-stop(0.52, rgb(115, 123, 162))
);
# 2.5 选择器
- [强制] 当一个 rule 包含多个 selector 时,每个选择器声明必须独占一行。
示例:
/* good */
.post,
.page,
.comment {
line-height: 1.5;
}
/* bad */
.post,
.page,
.comment {
line-height: 1.5;
}
- [强制]
>
、+
、~
选择器的两边各保留一个空格。
示例:
/* good */
main > nav {
padding: 10px;
}
label + input {
margin-left: 5px;
}
input:checked ~ button {
background-color: #69c;
}
/* bad */
main > nav {
padding: 10px;
}
label + input {
margin-left: 5px;
}
input:checked ~ button {
background-color: #69c;
}
- [强制] 属性选择器中的值必须用双引号包围。
解释:
不允许使用单引号,不允许不使用引号。
示例:
/* good */
article[character='juliet'] {
voice-family: 'Vivien Leigh', victoria, female;
}
/* bad */
article[character='juliet'] {
voice-family: 'Vivien Leigh', victoria, female;
}
# 2.6 属性
- [强制] 属性定义必须另起一行。
示例:
/* good */
.selector {
margin: 0;
padding: 0;
}
/* bad */
.selector {
margin: 0;
padding: 0;
}
- [强制] 属性定义后必须以分号结尾。
示例:
/* good */
.selector {
margin: 0;
}
/* bad */
.selector {
margin: 0;
}
# 3 通用
# 3.1 选择器
- [强制] 如无必要,不得为
id
、class
选择器添加类型选择器进行限定。
解释:
在性能和维护性上,都有一定的影响。
示例:
/* good */
#error,
.danger-message {
font-color: #c00;
}
/* bad */
dialog#error,
p.danger-message {
font-color: #c00;
}
- [建议] 选择器的嵌套层级应不大于 3 级,位置靠后的限定条件应尽可能精确。
示例:
/* good */
#username input {
}
.comment .avatar {
}
/* bad */
.page .header .login #username input {
}
.comment div * {
}
# 3.2 属性缩写
- [建议] 在可以使用缩写的情况下,尽量使用属性缩写。
示例:
/* good */
.post {
font: 12px/1.5 arial, sans-serif;
}
/* bad */
.post {
font-family: arial, sans-serif;
font-size: 12px;
line-height: 1.5;
}
- [建议] 使用
border
/margin
/padding
等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。
解释:
border / margin / padding 等缩写会同时设置多个属性的值,容易覆盖不需要覆盖的设定 。如某些方向需要继承其他声明的值,则应该分开设置。
示例:
/* centering <article class="page"> horizontally and highlight featured ones */
article {
margin: 5px;
border: 1px solid #999;
}
/* good */
.page {
margin-right: auto;
margin-left: auto;
}
.featured {
border-color: #69c;
}
/* bad */
.page {
margin: 5px auto; /* introducing redundancy */
}
.featured {
border: 1px solid #69c; /* introducing redundancy */
}
# 3.3 属性书写顺序
- [建议] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果) 的顺序书写,以提高代码的可读性。
解释:
- Formatting Model 相关属性包括:
position
/top
/right
/bottom
/left
/float
/display
/overflow
等 - Box Model 相关属性包括:
border
/margin
/padding
/width
/height
等 - Typographic 相关属性包括:
font
/line-height
/text-align
/word-wrap
等 - Visual 相关属性包括:
background
/color
/transition
/list-style
等
另外,如果包含 content
属性,应放在最前面。
示例:
.sidebar {
/* formatting model: positioning schemes / offsets / z-indexes / display / ... */
position: absolute;
top: 50px;
left: 0;
overflow-x: hidden;
/* box model: sizes / margins / paddings / borders / ... */
width: 200px;
padding: 5px;
border: 1px solid #ddd;
/* typographic: font / aligns / text styles / ... */
font-size: 14px;
line-height: 20px;
/* visual: colors / shadows / gradients / ... */
background: #f5f5f5;
color: #333;
-webkit-transition: color 1s;
-moz-transition: color 1s;
transition: color 1s;
}
# 3.4 清除浮动
- [建议] 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置
clear
或触发BFC
的方式进行clearfix
。尽量不使用增加空标签的方式。
解释:
触发 BFC 的方式很多,常见的有:
- float 非 none
- position 非 static
- overflow 非 visible
如希望使用更小副作用的清除浮动方法,参见 A new micro clearfix hack (opens new window) 一 文。
另需注意,对已经触发 BFC 的元素不需要再进行 clearfix。
# 3.5 !important
[建议] 尽量不使用
!important
声明。[建议] 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和
!important
定义样式。
解释:
必须注意的是,仅在设计上 确实不允许任何其它场景覆盖样式
时,才使用内联的
!important
样式。通常在第三方环境的应用中使用这种方案。下面的 z-index 章节是其
中一个特殊场景的典型样例。
# 3.6 z-index
- [建议] 将
z-index
进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。
解释:
同层的多个元素,如多个由用户输入触发的 Dialog,在该层级内使用相同的 z-index
或
递增 z-index
。
建议每层包含 100 个 z-index
来容纳足够的元素,如果每层元素较多,可以调整这个数
值。
- [建议] 在可控环境下,期望显示在最上层的元素,
z-index
指定为999999
。
解释:
可控环境分成两种,一种是自身产品线环境;还有一种是可能会被其他产品线引用,但是不 会被外部第三方的产品引用。
不建议取值为 2147483647
。以便于自身产品线被其他产品线引用时,当遇到层级覆盖冲
突的情况,留出向上调整的空间。
- [建议] 在第三方环境下,期望显示在最上层的元素,通过标签内联和
!important
,将z-index
指定为2147483647
。
解释:
第三方环境对于开发者来说完全不可控。在第三方环境下的元素,为了保证元素不被其页面 其他样式定义覆盖,需要采用此做法。
# 4 值与单位
# 4.1 文本
- [强制] 文本内容必须用双引号包围。
解释:
文本类型的内容可能在选择器、属性值等内容中。
示例:
/* good */
html[lang|='zh'] q:before {
font-family: 'Microsoft YaHei', sans-serif;
content: '“';
}
html[lang|='zh'] q:after {
font-family: 'Microsoft YaHei', sans-serif;
content: '”';
}
/* bad */
html[lang|='zh'] q:before {
font-family: 'Microsoft YaHei', sans-serif;
content: '“';
}
html[lang|='zh'] q:after {
font-family: 'Microsoft YaHei', sans-serif;
content: '”';
}
# 4.2 数值
- [强制] 当数值为 0 - 1 之间的小数时,省略整数部分的
0
。
示例:
/* good */
panel {
opacity: 0.8;
}
/* bad */
panel {
opacity: 0.8;
}
# 4.3 url()
- [强制]
url()
函数中的路径不加引号。
示例:
body {
background: url(bg.png);
}
- [建议]
url()
函数中的绝对路径可省去协议名。
示例:
body {
background: url(//baidu.com/img/bg.png) no-repeat 0 0;
}
# 4.4 长度
- [强制] 长度为
0
时须省略单位。 (也只有长度单位可省)
示例:
/* good */
body {
padding: 0 5px;
}
/* bad */
body {
padding: 0px 5px;
}
# 4.5 颜色
- [强制] RGB 颜色值必须使用十六进制记号形式
#rrggbb
。不允许使用rgb()
。
解释:
带有 alpha 的颜色信息可以使用 rgba()
。使用 rgba()
时每个逗号后必须保留一个空
格。
示例:
/* good */
.success {
box-shadow: 0 0 2px rgba(0, 128, 0, 0.3);
border-color: #008000;
}
/* bad */
.success {
box-shadow: 0 0 2px rgba(0, 128, 0, 0.3);
border-color: rgb(0, 128, 0);
}
- [强制] 颜色值可以缩写时,必须使用缩写形式。
示例:
/* good */
.success {
background-color: #aca;
}
/* bad */
.success {
background-color: #aaccaa;
}
- [强制] 颜色值不允许使用命名色值。
示例:
/* good */
.success {
color: #90ee90;
}
/* bad */
.success {
color: lightgreen;
}
- [建议] 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。
示例:
/* good */
.success {
background-color: #aca;
color: #90ee90;
}
/* good */
.success {
background-color: #aca;
color: #90ee90;
}
/* bad */
.success {
background-color: #aca;
color: #90ee90;
}
# 4.6 2D 位置
- [强制] 必须同时给出水平和垂直方向的位置。
解释:
2D 位置初始值为 0% 0%
,但在只有一个方向的值时,另一个方向的值会被解析为
center。为避免理解上的困扰,应同时给出两个方向的值
。background-position 属性值的定义 (opens new window)
示例:
/* good */
body {
background-position: center top; /* 50% 0% */
}
/* bad */
body {
background-position: top; /* 50% 0% */
}
# 5 文本编排
# 5.1 字体族
- [强制]
font-family
属性中的字体族名称应使用字体的英文Family Name
,其中如有空格,须放置在引号中。
解释:
所谓英文 Family Name,为字体文件的一个元数据,常见名称如下:
字体 | 操作系统 | Family Name |
---|---|---|
宋体 (中易宋体) | Windows | SimSun |
黑体 (中易黑体) | Windows | SimHei |
微软雅黑 | Windows | Microsoft YaHei |
微软正黑 | Windows | Microsoft JhengHei |
华文黑体 | Mac/iOS | STHeiti |
冬青黑体 | Mac/iOS | Hiragino Sans GB |
文泉驿正黑 | Linux | WenQuanYi Zen Hei |
文泉驿微米黑 | Linux | WenQuanYi Micro Hei |
示例:
h1 {
font-family: 'Microsoft YaHei';
}
- [强制]
font-family
按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族(serif
/sans-serif
)。
解释:
更详细说明可参考本文 (opens new window)。
示例:
/* Display according to platform */
.article {
font-family: Arial, sans-serif;
}
/* Specific for most platforms */
h1 {
font-family: 'Helvetica Neue', Arial, 'Hiragino Sans GB',
'WenQuanYi Micro Hei', 'Microsoft YaHei', sans-serif;
}
- [强制]
font-family
不区分大小写,但在同一个项目中,同样的Family Name
大小写必须统一。
示例:
/* good */
body {
font-family: Arial, sans-serif;
}
h1 {
font-family: Arial, 'Microsoft YaHei', sans-serif;
}
/* bad */
body {
font-family: arial, sans-serif;
}
h1 {
font-family: Arial, 'Microsoft YaHei', sans-serif;
}
# 5.2 字号
- [强制] 需要在 Windows 平台显示的中文内容,其字号应不小于
12px
。
解释:
由于 Windows 的字体渲染机制,小于 12px 的文字显示效果极差、难以辨认。
# 5.3 字体风格
- [建议] 需要在 Windows 平台显示的中文内容,不要使用除
normal
外的font-style
。其他平台也应慎用。
解释:
由于中文字体没有 italic 风格的实现,所有浏览器下都会 fallback 到 obilique 实现 ( 自动拟合为斜体),小字号下 (特别是 Windows 下会在小字号下使用点阵字体的情况下) 显 示效果差,造成阅读困难。
# 5.4 字重
- [强制]
font-weight
属性必须使用数值方式描述。
解释:
CSS 的字重分 100 – 900 共九档,但目前受字体本身质量和浏览器的限制,实际上支持 400 和 700 两档,分别等价于关键词 normal 和 bold。
浏览器本身使用一系 列启发式规则 (opens new window)来进行 匹配,在 <700 时一般匹配字体的 Regular 字重,>=700 时匹配 Bold 字重。
但已有浏览器开始支持 =600 时匹配 Semibold 字重 ( 见此表 (opens new window)),故使用数值描述增加 了灵活性,也更简短。
示例:
/* good */
h1 {
font-weight: 700;
}
/* bad */
h1 {
font-weight: bold;
}
# 5.5 行高
- [建议]
line-height
在定义文本段落时,应使用数值。
解释:
将 line-height 设置为数值,浏览器会基于当前元素设置的 font-size 进行再次计算。在 不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 font-size 都需要设置 line-height。
当 line-height 用于控制垂直居中时,还是应该设置成与容器高度一致。
示例:
.container {
line-height: 1.5;
}
# 6 变换与动画
- [强制] 使用
transition
时应指定transition-property
。
示例:
/* good */
.box {
transition: color 1s, border-color 1s;
}
/* bad */
.box {
transition: all 1s;
}
- [建议] 尽可能在浏览器能高效实现的属性上添加过渡和动画。
解释:
见本文 (opens new window), 在可能的情况下应选择这样四种变换:
transform: translate(npx, npx);
transform: scale(n);
transform: rotate(ndeg);
opacity: 0..1;
典型的,可以使用 translate 来代替 left 作为动画属性。
示例:
/* good */
.box {
transition: transform 1s;
}
.box:hover {
transform: translate(20px); /* move right for 20px */
}
/* bad */
.box {
left: 0;
transition: left 1s;
}
.box:hover {
left: 20px; /* move right for 20px */
}
# 7 响应式
- [强制]
Media Query
不得单独编排,必须与相关的规则一起定义。
示例:
/* Good */
/* header styles */
@media (...) {
/* header styles */
}
/* main styles */
@media (...) {
/* main styles */
}
/* footer styles */
@media (...) {
/* footer styles */
}
/* Bad */
/* header styles */
/* main styles */
/* footer styles */
@media (...) {
/* header styles */
/* main styles */
/* footer styles */
}
- [强制]
Media Query
如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。
示例:
@media (-webkit-min-device-pixel-ratio: 2),
/* Webkit-based browsers */ (min--moz-device-pixel-ratio: 2),
/* Older Firefox browsers (prior to firefox 16) */ (min-resolution: 2dppx),
/* The standard way */ (min-resolution: 192dpi) {
/* dppx fallback */
/* Retina-specific stuff here */
}
- [建议] 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。
# 8 兼容性
# 8.1 属性前缀
- [强制] 带私有前缀的属性由长到短排列,按冒号位置对齐。
解释:
标准属性放在最后,按冒号对齐方便阅读,也便于在编辑器内进行多行编辑。
示例:
.box {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
# 8.2 Hack
- [建议] 需要添加
hack
时应尽可能考虑是否可以采用其他方式解决。
解释:
如果能通过合理的 HTML 结构或使用其他的 CSS 定义达到理想的样式,则不应该使用 hack 手段解决问题。通常 hack 会导致维护成本的增加。
- [建议] 尽量使用
选择器 hack
处理兼容性,而非属性 hack
。
解释:
尽量使用符合 CSS 语法的 selector hack,可以避免一些第三方库无法识别 hack 语法的 问题。
- [建议] 尽量使用简单的
属性 hack
。
示例:
.box {
_display: inline; /* fix double margin */
float: left;
margin-left: 20px;
}
.container {
overflow: hidden;
*zoom: 1; /* triggering hasLayout */
}
# 9 注释规范
# 9.1 单行注释
注释内容第一个字符和最后一个字符都是一个空格字符,单独占一行,行与行之间相隔一行 示例:
/* Comment Text */
.jdc {
}
/* Comment Text */
.jdc {
}
# 9.2 模块注释
注释内容第一个字符和最后一个字符都是一个空格字符,/_ 与 模块信息描述占一行,多 个横线分隔符-与_/占一行,行与行之间相隔两行示例:
/* Module A
---------------------------------------------------------------- */
.mod_a {
}
/* Module B
---------------------------------------------------------------- */
.mod_b {
}
# 9.3 文件信息注释
在样式文件编码声明 @charset 语句下面注明页面名称、作者、创建日期等信息示例
@charset "UTF-8";
/**
* @desc File Info
* @author Author Name
* @date 2015-10-10
*/
# 10 SASS 规范
# 10.1 语法选用
SASS 有两种语法格式,一种是 SCSS (Sassy CSS),另一种是缩进格式(Indented Syntax),有时称之为 Sass。 SCSS SCSS 语法基于 CSS 语法扩展,每一个有效的 CSS 文件都是一个有效的具有相同含义的 SCSS 文件,换种说法就是 SCSS 能识别大多数的 CSS hacks 写法和浏览器前缀写法以及早期的 IE 滤镜写法,这种格式以 .scss 作为扩展名。 Sass Sass 使用 “缩进” 代替 “花括号” 表示属性属于某个选择器,用 “换行” 代替 “分 号” 分隔属性,很多人认为这样做比 SCSS 更容易阅读,书写也更快速。缩进格式也可以使 用 Sass 的全部功能,只是与 SCSS 相比个别地方采取了不同的表达方式。这种格式以 .sass 作为拓展名。考虑到 SCSS 语法对 CSS 语法友好的兼容性和扩展性,我们在使用 SASS 编写样式的时候,统一使用 SCSS 语法
# 10.2 注释规范
SCSS 文件内 全部遵循 CSS 注释规范 不使用 /_! _/ 注释方式 注释内不放 SASS 变量标准的注释规范如下:
@charset "UTF-8";
/**
* @desc File Info
* @author liqinuo
* @date 2015-10-10
*/
/* Module A
----------------------------------------------------------------*/
.mod_a {
}
/* module A logo */
.mod_a_logo {
}
/* module A nav */
.mod_a_nav {
}
/* Module B
----------------------------------------------------------------*/
.mod_b {
}
/* module B logo */
.mod_b_logo {
}
/* module B nav */
.mod_b_nav {
}
# 10.3 嵌套规范
- 10.3.1 选择器嵌套
/* CSS */
.jdc {
}
body .jdc {
}
/* SCSS */
.jdc {
body & {
}
}
/* CSS */
.jdc {
}
.jdc_cover {
}
.jdc_info {
}
.jdc_info_name {
}
/* SCSS */
.jdc {
&_cover {
}
&_info {
&_name {
}
}
}
- 10.3.2 属性嵌套
/* CSS */
.jdc {
background-color: red;
background-repeat: no-repeat;
background-image: url(/img/icon.png);
background-position: 0 0;
}
/* SCSS */
.jdc {
background: {
color: red;
repeat: no-repeat;
image: url(/img/icon.png);
position: 0 0;
}
}
# 10.4 变量
可复用属性尽量抽离为页面变量,易于统一维护
// CSS
.jdc {
color: red;
border-color: red;
}
// SCSS
$color: red;
.jdc {
color: $color;
border-color: $color;
}
# 10.5 混合
根据功能定义模块,然后在需要使用的地方通过 @include 调用,避免编码时重复输入代 码段
// CSS
.jdc_1 {
-webkit-border-radius: 5px;
border-radius: 5px;
}
.jdc_2 {
-webkit-border-radius: 10px;
border-radius: 10px;
}
// SCSS
@mixin radius($radius: 5px) {
-webkit-border-radius: $radius;
border-radius: $radius;
}
.jdc_1 {
@include radius; //参数使用默认值
}
.jdc_2 {
@include radius(10px);
}
// CSS
.jdc_1 {
background: url(/img/icon.png) no-repeat -10px 0;
}
.jdc_2 {
background: url(/img/icon.png) no-repeat -20px 0;
}
// SCSS
@mixin icon($x: 0, $y: 0) {
background: url(/img/icon.png) no-repeat $x, $y;
}
.jdc_1 {
@include icon(-10px, 0);
}
.jdc_2 {
@include icon(-20px, 0);
}
# 10.6 占位选择器
如果不调用则不会有任何多余的 css 文件,占位选择器以 % 标识定义,通过 @extend 调用
//scss
%borderbox {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.jdc {
@extend %borderbox;
}
# 10.7 extend 继承
// CSS
.jdc_1 {
font-size: 12px;
color: red;
}
.jdc_2 {
font-size: 12px;
color: red;
font-weight: bold;
}
// SCSS
.jdc_1 {
font-size: 12px;
color: red;
}
.jdc_2 {
@extend .jdc_1;
font-weight: bold;
}
// 或者
%font_red {
font-size: 12px;
color: red;
}
.jdc_1 {
@extend %font_red;
}
.jdc_2 {
@extend %font_red;
font-weight: bold;
}
# 10.8 for 循环
// CSS
.jdc_1 {
background-position: 0 -20px;
}
.jdc_2 {
background-position: 0 -40px;
}
.jdc_3 {
background-position: 0 -60px;
}
// SCSS
@for $i from 1 through 3 {
.jdc_#{$i} {
background-position: 0 (-20px) * $i;
}
}
注意:#{} 是连接符,变量连接使用时需要依赖
# 10.9 each 循环
// CSS
.jdc_list {
background-image: url(/img/jdc_list.png);
}
.jdc_detail {
background-image: url(/img/jdc_detail.png);
}
// SCSS
@each $name in list, detail {
.jdc_#{$name} {
background-image: url(/img/jdc_#{$name}.png);
}
}
// CSS
.jdc_list {
background-image: url(/img/jdc_list.png);
background-color: red;
}
.jdc_detail {
background-image: url(/img/jdc_detail.png);
background-color: blue;
}
// SCSS
@each $name, $color in (list, red), (detail, blue) {
.jdc_#{$name} {
background-image: url(/img/jdc_#{$name}.png);
background-color: $color;
}
}
# 10.10 function 函数
@function pxToRem($px) {
@return $px / 10px * 1rem;
}
.jdc {
font-size: pxToRem(12px);
}
# 10.11 运算规范
运算符之间空出一个空格
.jdc {
width: 100px - 50px;
height: 30px / 5;
}
注意运算单位,单位同时参与运算,所以 10px 不等于 10,乘除运算时需要特别注意
// 正确的运算格式
.jdc {
width: 100px - 50px;
width: 100px + 50px;
width: 100px * 2;
width: 100px / 2;
}
# HTML编码规范
# 1 前言
HTML 作为描述网页结构的超文本标记语言,一直有着广泛的应用。本文档的目标是使 HTML 代码风格保持一致,容易被理解和被维护。
# 2 代码风格
# 2.1 缩进与换行
- [强制] 使用
4
个空格做为一个缩进层级,不允许使用2
个空格 或tab
字符。
示例:
<ul>
<li>first</li>
<li>second</li>
</ul>
- [建议] 每行不得超过
120
个字符。
解释:
过长的代码不容易阅读与维护。但是考虑到 HTML 的特殊性,不做硬性要求。
# 2.2 命名
[强制]
class
必须单词全字母小写,单词间以-
分隔。[强制]
class
必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。
示例:
<!-- good -->
<div class="sidebar"></div>
<!-- bad -->
<div class="left"></div>
- [强制] 元素
id
必须保证页面唯一。
解释:
同一个页面中,不同的元素包含相同的 id,不符合 id 的属性含义。并且使用 document.getElementById 时可能导致难以追查的问题。
[建议]
id
建议单词全字母小写,单词间以-
分隔。同项目必须保持风格一致。[建议]
id
、class
命名,在避免冲突并描述清楚的前提下尽可能短。
示例:
<!-- good -->
<div id="nav"></div>
<!-- bad -->
<div id="navigation"></div>
<!-- good -->
<p class="comment"></p>
<!-- bad -->
<p class="com"></p>
<!-- good -->
<span class="author"></span>
<!-- bad -->
<span class="red"></span>
- [强制] 禁止为了
hook 脚本
,创建无样式信息的class
。
解释:
不允许 class 只用于让 JavaScript 选择某些元素,class 应该具有明确的语义和样式。 否则容易导致 css class 泛滥。
使用 id、属性选择作为 hook 是更好的方式。
- [强制] 同一页面,应避免使用相同的
name
与id
。
解释:
IE 浏览器会混淆元素的 id 和 name 属性, document.getElementById 可能获得不期望的 元素。所以在对元素的 id 与 name 属性的命名需要非常小心。
一个比较好的实践是,为 id 和 name 使用不同的命名法。
示例:
<input name="foo" />
<div id="foo"></div>
<script>
// IE6 将显示 INPUT
alert(document.getElementById('foo').tagName)
</script>
# 2.3 标签
- [强制] 标签名必须使用小写字母。
示例:
<!-- good -->
<p>Hello StyleGuide!</p>
<!-- bad -->
<p>Hello StyleGuide!</p>
- [强制] 对于无需自闭合的标签,不允许自闭合。
解释:
常见无需自闭合标签有 input、br、img、hr 等。
示例:
<!-- good -->
<input type="text" name="title" />
<!-- bad -->
<input type="text" name="title" />
- [强制] 对
HTML5
中规定允许省略的闭合标签,不允许省略闭合标签。
解释:
对代码体积要求非常严苛的场景,可以例外。比如:第三方页面使用的投放系统。
示例:
<!-- good -->
<ul>
<li>first</li>
<li>second</li>
</ul>
<!-- bad -->
<ul>
<li>first</li>
<li>second</li>
</ul>
- [强制] 标签使用必须符合标签嵌套规则。
解释:
比如 div 不得置于 p 中,tbody 必须置于 table 中。
详细的标签嵌套规则参见HTML DTD (opens new window)中的
Elements
定义部分。
- [建议]
HTML
标签的使用应该遵循标签的语义。
解释:
下面是常见标签语义
- p - 段落
- h1,h2,h3,h4,h5,h6 - 层级标题
- strong,em - 强调
- ins - 插入
- del - 删除
- abbr - 缩写
- code - 代码标识
- cite - 引述来源作品的标题
- q - 引用
- blockquote - 一段或长篇引用
- ul - 无序列表
- ol - 有序列表
- dl,dt,dd - 定义列表
示例:
<!-- good -->
<p>
Esprima serves as an important <strong>building block</strong> for some
JavaScript language tools.
</p>
<!-- bad -->
<div>
Esprima serves as an important <span class="strong">building block</span> for
some JavaScript language tools.
</div>
- [建议] 在
CSS
可以实现相同需求的情况下不得使用表格进行布局。
解释:
在兼容性允许的情况下应尽量保持语义正确性。对网格对齐和拉伸性有严格要求的场景允许 例外,如多列复杂表单。
- [建议] 标签的使用应尽量简洁,减少不必要的标签。
示例:
<!-- good -->
<img class="avatar" src="image.png" />
<!-- bad -->
<span class="avatar">
<img src="image.png" />
</span>
# 2.4 属性
- [强制] 属性名必须使用小写字母。
示例:
<!-- good -->
<table cellspacing="0">
...
</table>
<!-- bad -->
<table cellspacing="0">
...
</table>
- [强制] 属性值必须用双引号包围。
解释:
不允许使用单引号,不允许不使用引号。
示例:
<!-- good -->
<script src="esl.js"></script>
<!-- bad -->
<script src="esl.js"></script>
<script src="esl.js"></script>
- [建议] 布尔类型的属性,建议补全属性值。
示例:
<input type="text" disabled="true" />
<input type="checkbox" value="1" checked="true" />
- [建议] 自定义属性建议以
xxx-
为前缀,推荐使用data-
。
解释:
使用前缀有助于区分自定义属性和标准定义的属性。
示例:
<ol data-ui-type="Select"></ol>
# 3 通用
# 3.1 DOCTYPE
- [强制] 使用
HTML5
的doctype
来启用标准模式,建议使用大写的DOCTYPE
。
示例:
<!DOCTYPE html>
- [建议] 启用 IE Edge 模式。
示例:
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
- [建议] 在
html
标签上设置正确的 lang 属性。
解释:
有助于提高页面的可访问性,如:让语音合成工具确定其所应该采用的发音,令翻译工具确 定其翻译语言等。
示例:
<html lang="zh-CN"></html>
# 3.2 编码
- [强制] 页面必须使用精简形式,明确指定字符编码。指定字符编码的
meta
必须是head
的第一个直接子元素。
示例:
<html>
<head>
<meta charset="UTF-8" />
......
</head>
<body>
......
</body>
</html>
- [建议]
HTML
文件使用无BOM
的UTF-8
编码。
解释:
UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰 。
# 3.3 CSS 和 JavaScript 引入
- [强制] 引入
CSS
时必须指明rel="stylesheet"
。
示例:
<link rel="stylesheet" src="page.css" />
- [建议] 引入
CSS
和JavaScript
时无须指明type
属性。
解释:
text/css
和 text/javascript
是 type 的默认值。
- [建议] 展现定义放置于外部
CSS
中,行为定义放置于外部JavaScript
中。
解释:
结构-样式-行为的代码分离,对于提高代码的可阅读性和维护性都有好处。
- [建议] 在
head
中引入页面需要的所有CSS
资源。
解释:
在页面渲染的过程中,新的 CSS 可能导致元素的样式重新计算和绘制,页面闪烁。
- [建议]
JavaScript
应当放在页面末尾,或采用异步加载。
解释:
将 script 放在页面中间将阻断页面的渲染。出于性能方面的考虑,如非必要,请遵守此条 建议。
示例:
<body>
<!-- a lot of elements -->
<script src="init-behavior.js"></script>
</body>
- [建议] 移动环境或只针对现代浏览器设计的 Web 应用,如果引用外部资源的
URL
协议部分与页面相同,建议省略协议前缀。
解释:
使用 protocol-relative URL
引入 CSS,在 IE7/8
下,会发两次请求。是否使用
protocol-relative URL
应充分考虑页面针对的环境。
示例:
<script src="//s1.bdstatic.com/cache/static/jquery-1.10.2.min_f2fb5194.js"></script>
# 4 head
# 4.1 title
[强制] 页面必须包含
title
标签声明标题。[强制]
title
必须作为head
的直接子元素,并紧随charset
声明之后。
解释:
title 中如果包含 ascii 之外的字符,浏览器需要知道字符编码类型才能进行解码,否则 可能导致乱码。
示例:
<head>
<meta charset="UTF-8" />
<title>页面标题</title>
</head>
# 4.2 favicon
- [强制] 保证
favicon
可访问。
解释:
在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 favicon.ico 。为了 保证 favicon 可访问,避免 404,必须遵循以下两种方法之一:
- 在 Web Server 根目录放置 favicon.ico 文件。
- 使用 link 指定 favicon。
示例:
<link rel="shortcut icon" href="path/to/favicon.ico" />
# 4.3 viewport
- [建议] 若页面欲对移动设备友好,需指定页面的
viewport
。
解释:
viewport meta tag 可以设置可视区域的宽度和初始缩放大小,避免在移动设备上出现页面 展示不正常。
比如,在页面宽度小于 980px 时,若需 iOS 设备友好,应当设置 viewport 的 width 值 来适应你的页面宽度。同时因为不同移动设备分辨率不同,在设置时,应当使用 device-width 和 device-height 变量。
另外,为了使 viewport 正常工作,在页面内容样式布局设计上也要做相应调整,如避免绝 对定位等。
# 5 图片
- [强制] 禁止
img
的src
取值为空。延迟加载的图片也要增加默认的src
。
解释:
src 取值为空,会导致部分浏览器重新加载一次当前页面。
- [建议] 避免为
img
添加不必要的title
属性。
解释:
多余的 title 影响看图体验,并且增加了页面尺寸。
- [建议] 为重要图片添加
alt
属性。
解释:
可以提高图片加载失败时的用户体验。
[建议] 添加
width
和height
属性,以避免页面抖动。[建议] 有下载需求的图片采用
img
标签实现,无下载需求的图片采用CSS
背景图实现。
解释:
- 产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 img 形式实现, 能方便用户下载。
- 无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 css 背景图实 现。
# 6 表单
# 6.1 控件标题
- [强制] 有文本标题的控件必须使用
label
标签将其与其标题相关联。
解释:
有两种方式:
- 将控件置于 label 内。
- label 的 for 属性指向控件的 id。
推荐使用第一种,减少不必要的 id。如果 DOM 结构不允许直接嵌套,则应使用第二种。
示例:
<label
><input type="checkbox" name="confirm" value="on" /> 我已确认上述条款</label
>
<label for="username">用户名:</label>
<input type="textbox" name="username" id="username" />
# 6.2 按钮
- [强制] 使用
button
元素时必须指明type
属性值。
解释:
button 元素的默认 type 为 submit,如果被置于 form 元素中,点击后将导致表单提交。 为显示区分其作用方便理解,必须给出 type 属性。
示例:
<button type="submit">提交</button> <button type="button">取消</button>
- [建议] 尽量不要使用按钮类元素的
name
属性。
解释:
由于浏览器兼容性问题,使用按钮的 name 属性会带来许多难以发现的问题。
# 6.3 可访问性 (A11Y)
- [建议] 负责主要功能的按钮在
DOM
中的顺序应靠前。
解释:
负责主要功能的按钮应相对靠前,以提高可访问性。如果在 CSS 中指定了 float: right
则可能导致视觉上主按钮在前,而 DOM 中主按钮靠后的情况。
示例:
<!-- good -->
<style>
.buttons .button-group {
float: right;
}
</style>
<div class="buttons">
<div class="button-group">
<button type="submit">提交</button>
<button type="button">取消</button>
</div>
</div>
<!-- bad -->
<style>
.buttons button {
float: right;
}
</style>
<div class="buttons">
<button type="button">取消</button>
<button type="submit">提交</button>
</div>
- [建议] 当使用
JavaScript
进行表单提交时,如果条件允许,应使原生提交功能正常工作。
解释:
当浏览器 JS 运行错误或关闭 JS 时,提交功能将无法工作。如果正确指定了 form 元素的 action 属性和表单控件的 name 属性时,提交仍可继续进行。
示例:
<form action="/login" method="post">
<p><input name="username" type="text" placeholder="用户名" /></p>
<p><input name="password" type="password" placeholder="密码" /></p>
</form>
- [建议] 在针对移动设备开发的页面时,根据内容类型指定输入框的
type
属性。
解释:
根据内容类型指定输入框类型,能获得能友好的输入体验。
示例:
<input type="date" />
# 7 多媒体
- [建议] 当在现代浏览器中使用
audio
以及video
标签来播放音频、视频时,应当注意格式。
解释:
音频应尽可能覆盖到如下格式:
- MP3
- WAV
- Ogg
视频应尽可能覆盖到如下格式:
MP4
WebM
Ogg
[建议] 在支持
HTML5
的浏览器中优先使用audio
和video
标签来定义音视频元素。[建议] 使用退化到插件的方式来对多浏览器进行支持。
示例:
<audio controls>
<source src="audio.mp3" type="audio/mpeg" />
<source src="audio.ogg" type="audio/ogg" />
<object width="100" height="50" data="audio.mp3">
<embed width="100" height="50" src="audio.swf" />
</object>
</audio>
<video width="100" height="50" controls>
<source src="video.mp4" type="video/mp4" />
<source src="video.ogg" type="video/ogg" />
<object width="100" height="50" data="video.mp4">
<embed width="100" height="50" src="video.swf" />
</object>
</video>
[建议] 只在必要的时候开启音视频的自动播放。
[建议] 在
object
标签内部提供指示浏览器不支持该标签的说明。
示例:
<object width="100" height="50" data="something.swf">
DO NOT SUPPORT THIS TAG
</object>
# 8 模板中的 HTML
- [建议] 模板代码的缩进优先保证
HTML
代码的缩进规则。
示例:
<!-- good -->
{if $display == true}
<div>
<ul>
{foreach $item_list as $item}
<li>{$item.name}</li>
<li>{/foreach}</li>
</ul>
</div>
{/if}
<!-- bad -->
{if $display == true}
<div>
<ul>
{foreach $item_list as $item}
<li>{$item.name}</li>
<li>{/foreach}</li>
</ul>
</div>
{/if}
- [建议] 模板代码应以保证
HTML
单个标签语法的正确性为基本原则。
示例:
<!-- good -->
<li class="{if $item.type_id == $current_type}focus{/if}">{ $item.type_name }</li>
<!-- bad -->
<li {if $item.type_id == $current_type} class="focus"{/if}>{ $item.type_name }</li>
- [建议] 在循环处理模板数据构造表格时,若要求每行输出固定的个数,建议先将数据分组,之后再循环输出。
示例:
<!-- good -->
<table>
{foreach $item_list as $item_group}
<tr>
{foreach $item_group as $item}
<td>{ $item.name }</td>
{/foreach}
</tr>
<tr>
{/foreach}
</tr>
</table>
<!-- bad -->
<table>
<tr>
{foreach $item_list as $item}
<td>{ $item.name }</td>
{if $item@iteration is div by 5}
</tr>
<tr>
{/if} {/foreach}
</tr>
</table>
# 9 注释规范
# 9.1 单行注释
一般用于简单的描述,如某些状态描述、属性描述等注释内容前后各一个空格字符,注释位 于要注释代码的上面,单独占一行推荐:
<!-- Comment Text -->
<div>...</div>
不推荐:
<div>...</div>
<!-- Comment Text -->
<div>
<!-- Comment Text -->
...
</div>
# 9.2 模块注释
一般用于描述模块的名称以及模块开始与结束的位置,注释内容前后各一个空格字符,
<!-- S Comment Text -->
表示模块开始,
<!-- E Comment Text -->
表示模块结束,模块与模块之间相隔一行
推荐:
<!-- S Comment Text A -->
<div class="mod_a">...</div>
<!-- E Comment Text A -->
<!-- S Comment Text B -->
<div class="mod_b">...</div>
<!-- E Comment Text B -->
不推荐
<!-- S Comment Text A -->
<div class="mod_a">...</div>
<!-- E Comment Text A -->
<!-- S Comment Text B -->
<div class="mod_b">...</div>
<!-- E Comment Text B -->
# 9.3 嵌套模块注释
当模块注释内再出现模块注释的时候,为了突出主要模块,嵌套模块不再使用
<!-- S Comment Text -->
<!-- E Comment Text -->
而改用
<!-- /Comment Text -->
注释写在模块结尾标签底部,单独一行。
<!-- S Comment Text A -->
<div class="mod_a">
<div class="mod_b">...</div>
<!-- /mod_b -->
<div class="mod_c">...</div>
<!-- /mod_c -->
</div>
<!-- E Comment Text A -->
# 10 文件模版
# 10.1 PC 端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="your description" />
<meta name="author" content="author,email address" />
<meta name="robots" content="index,follow" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<meta name="renderer" content="ie-stand" />
<title>PC端HTML模版</title>
<!-- S DNS预解析 -->
<link rel="dns-prefetch" href="" />
<!-- E DNS预解析 -->
<!-- S 线上样式页面片,开发请直接取消注释引用 -->
<!-- #include virtual="" -->
<!-- E 线上样式页面片 -->
<!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
<link rel="stylesheet" href="css/index.css" />
<!-- /本地调试方式 -->
<link rel="stylesheet" href="http://srcPath/index.css" />
<!-- /开发机调试方式 -->
<!-- E 本地调试 -->
</head>
<body></body>
</html>
# 10.2 移动端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no"
/>
<meta name="format-detection" content="telephone=no" />
<title>移动端HTML模版</title>
<!-- S DNS预解析 -->
<link rel="dns-prefetch" href="" />
<!-- E DNS预解析 -->
<!-- S 线上样式页面片,开发请直接取消注释引用 -->
<!-- #include virtual="" -->
<!-- E 线上样式页面片 -->
<!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
<link rel="stylesheet" href="css/index.css" />
<!-- /本地调试方式 -->
<link rel="stylesheet" href="http://srcPath/index.css" />
<!-- /开发机调试方式 -->
<!-- E 本地调试 -->
</head>
<body></body>
</html>
# JavaScript编码规范
# 1 前言
# 2 文件命名规范
# 2.1 目录命名规范
- 项目文件夹:projectname
- 样式文件夹:css
- 脚本文件夹:js
- 样式类图片文件夹:img
# 2.2 图片命名规范
图片命名建议以以下顺序命名:
图片业务(可选) +(mod_)图片功能类别(必选)+ 图片模块名称(可选) + 图片精度 (可选)
图片业务:
- yw_:医为
- pp_:拍拍
- wx_:微信
- sq_:手 Q
- jd_:京东商城
- …
图片功能类别:
- mod_:是否公共,可选
- icon:模块类固化的图标
- logo:LOGO 类
- spr:单页面各种元素合并集合
- btn:按钮
- bg:可平铺或者大背景
- …
图片模块名称:
- goodslist:商品列表
- goodsinfo:商品信息
- userava tar:用户头像
- …
图片精度:
- 普清:@1x
- Retina:@2x | @3x
- …
如下面例子:
公共模块:
yw_mod_btn_goodlist@2x.png
yw_mod_btn_goodlist.png
mod_btn_goodlist.png
非公共模块:
yw_btn_goodlist@2x.png
yw_btn_goodlist.png
btn_goodlist.png
# 2.3 HTML/CSS 命名规范
确保文件命名总是以字母开头而不是数字,且字母一律小写,以下划线连接且不带其他标点 符号,如:
<!-- HTML -->
jdc.html
jdc_list.html
jdc_detail.html
<!-- SASS -->
jdc.scss
jdc_list.scss
jdc_detail.scss
# 2.4 className 命名规范
ClassName 的命名应该尽量精短、明确,必须以字母开头命名,且全部字母为小写,单词之
间统一使用下划线 “_
” 连接
# 2.5 命名原则
基于姓氏命名法(继承 + 外来),祖先模块不能出现下划线,除了是全站公用模块,如
mod_
系列的命名:
推荐:
<div class="modulename">
<div class="modulename_info">
<div class="modulename_son"></div>
<div class="modulename_son"></div>
...
</div>
</div>
<!-- 这个是全站公用模块,祖先模块允许直接出现下划线 -->
<div class="mod_info">
<div class="mod_info_son"></div>
<div class="mod_info_son"></div>
...
</div>
不推荐:
<div class="modulename_info">
<div class="modulename_info_son"></div>
<div class="modulename_info_son"></div>
...
</div>
在子孙模块数量可预测的情况下,严格继承祖先模块的命名前缀
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info"></div>
</div>
当子孙模块超过 4 级或以上的时候,可以考虑在祖先模块内具有识辨性的独立缩写作为新 的子孙模块
推荐:
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info">
<div class="modulename_info_user">
<div class="modulename_info_user_img">
<img src="" alt="" />
<!-- 这个时候 miui 为 modulename_info_user_img 首字母缩写-->
<div class="miui_tit"></div>
<div class="miui_txt"></div>
...
</div>
</div>
<div class="modulename_info_list"></div>
</div>
</div>
不推荐:
<div class="modulename">
<div class="modulename_cover"></div>
<div class="modulename_info">
<div class="modulename_info_user">
<div class="modulename_info_user_img">
<img src="" alt="" />
<div class="modulename_info_user_img_tit"></div>
<div class="modulename_info_user_img_txt"></div>
...
</div>
</div>
<div class="modulename_info_list"></div>
</div>
</div>
- 2.5.1 模块命名
全站公共模块:以 mod_
开头
<div class="mod_yours"></div>
业务公共模块:以 业务名_mod_
开头
<div class="paipai_mod_yours"></div>
- 2.5.2 常用命名推荐
注意:ad
、banner
、gg
、guanggao
等有机会和广告挂勾的字眠不建议直接用来做
ClassName,因为有些浏览器插件(Chrome 的广告拦截插件等)会直接过滤这些类名,因此
<div class="ad"></div>
这种广告的英文或拼音类名不应该出现
另外,敏感不和谐字眼也不应该出现,如:
<div class="fuck"></div>
<div class="jer"></div>
<div class="sm"></div>
<div class="gcd"></div>
<div class="ass"></div>
<div class="KMT"></div>
...
ClassName | 含义 |
---|---|
about | 关于 |
account | 账户 |
arrow | 箭头图标 |
article | 文章 |
aside | 边栏 |
audio | 音频 |
avatar | 头像 |
bg,background | 背景 |
bar | 栏(工具类) |
branding | 品牌化 |
crumb,breadcrumbs | 面包屑 |
btn,button | 按钮 |
caption | 标题,说明 |
category | 分类 |
chart | 图表 |
clearfix | 清除浮动 |
close | 关闭 |
col,column | 列 |
comment | 评论 |
community | 社区 |
container | 容器 |
content | 内容 |
copyright | 版权 |
current | 当前态,选中态 |
default | 默认 |
description | 描述 |
details | 细节 |
disabled | 不可用 |
entry | 文章,博文 |
error | 错误 |
even | 偶数,常用于多行列表或表格中 |
fail | 失败(提示) |
feature | 专题 |
fewer | 收起 |
field | 用于表单的输入区域 |
figure | 图 |
filter | 筛选 |
first | 第一个,常用于列表中 |
footer | 页脚 |
forum | 论坛 |
gallery | 画廊 |
group | 模块,清除浮动 |
header | 页头 |
help | 帮助 |
hide | 隐藏 |
hightlight | 高亮 |
home | 主页 |
icon | 图标 |
info,information | 信息 |
last | 最后一个,常用于列表中 |
links | 链接 |
login | 登录 |
logout | 退出 |
logo | 标志 |
main | 主体 |
menu | 菜单 |
meta | 作者、更新时间等信息栏,一般位于标题之下 |
module | 模块 |
more | 更多(展开) |
msg,message | 消息 |
nav,navigation | 导航 |
next | 下一页 |
nub | 小块 |
odd | 奇数,常用于多行列表或表格中 |
off | 鼠标离开 |
on | 鼠标移过 |
output | 输出 |
pagination | 分页 |
pop,popup | 弹窗 |
preview | 预览 |
previous | 上一页 |
primary | 主要 |
progress | 进度条 |
promotion | 促销 |
rcommd,recommendations | 推荐 |
reg,register | 注册 |
save | 保存 |
search | 搜索 |
secondary | 次要 |
section | 区块 |
selected | 已选 |
share | 分享 |
show | 显示 |
sidebar | 边栏,侧栏 |
slide | 幻灯片,图片切换 |
sort | 排序 |
sub | 次级的,子级的 |
submit | 提交 |
subscribe | 订阅 |
subtitle | 副标题 |
success | 成功(提示) |
summary | 摘要 |
tab | 标签页 |
table | 表格 |
txt,text | 文本 |
thumbnail | 缩略图 |
time | 时间 |
tips | 提示 |
title | 标题 |
video | 视频 |
wrap | 容器,包,一般用于最外层 |
wrapper | 容器,包,一般用于最外层 |
# 3 编码规则
# 3.1 编码
- [建议]
JavaScript
文件使用无BOM
的UTF-8
编码。
解释:
UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰 。
- [建议] 在文件结尾处,保留一个空行。
# 3.2 缩进
[强制] 使用
4
个空格做为一个缩进层级,不允许使用2
个空格 或tab
字符。[强制]
switch
下的case
和default
必须增加一个缩进层级。
示例:
// good
switch (variable) {
case '1':
// do...
break;
case '2':
// do...
break;
default:
// do...
}
// bad
switch (variable) {
case '1':
// do...
break;
case '2':
// do...
break;
default:
// do...
}
# 3.3 空格
- [强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
示例:
var a = !arr.length;
a++;
a = b + c
- [强制] 用作代码块起始的左花括号
{
前必须有一个空格。
示例:
// good
if (condition) {
}
while (condition) {}
function funcName() {}
// bad
if (condition) {
}
while (condition) {}
function funcName() {}
- [强制]
if / else / for / while / function / switch / do / try / catch / finally
关键字后,必须有一个空格。
示例:
// good
if (condition) {
}
while (condition) {}
(function () {})();
// bad
if (condition) {
}
while (condition) {}
(function () {})()```
- [强制] 在对象创建时,属性中的 `;:` 之后必须有空格,`;:` 之前不允许有空格。
示例:
```;javascript;
// good
var obj = {
a: 1,
b: 2,
c: 3,
};
// bad
var obj = {
a: 1,
b: 2,
c: 3,
}
- [强制] 函数声明、具名函数表达式、函数调用中,函数名和
(
之间不允许有空格。
示例:
// good
function funcName() {}
var funcName = function funcName() {};
funcName();
// bad
function funcName() {}
var funcName = function funcName() {};
funcName()
- [强制]
,
和;
前不允许有空格。
示例:
// good
callFunc(a, b);
// bad
callFunc(a, b)
- [强制] 在函数调用、函数声明、括号表达式、属性访问、
if / for / while / switch / catch
等语句中,()
和[]
内紧贴括号部分不允许有空格。
示例:
// good
callFunc(param1, param2, param3);
save(this.list[this.indexes[i]]);
needIncream && (variable += increament);
if (num > list.length) {
}
while (len--) {}
// bad
callFunc(param1, param2, param3);
save(this.list[this.indexes[i]]);
needIncreament && (variable += increament);
if (num > list.length) {
}
while (len--) {}
- [强制] 单行声明的数组与对象,如果包含元素,
{}
和[]
内紧贴括号部分不允许包含空格。
解释:
声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复 杂的情况,还是应该换行书写。
示例:
// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = { name: 'obj' };
var obj3 = {
name: 'obj',
age: 20,
sex: 1,
};
// bad
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = { name: 'obj' };
var obj3 = { name: 'obj', age: 20, sex: 1 }
- [强制] 行尾不得有多余的空格。
# 3.4 换行
[强制] 每个独立语句结束后必须换行。
[强制] 每行不得超过
120
个字符。
解释:
超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。
- [强制] 运算符处换行时,运算符必须在新行的行首。
示例:
// good
if (
(user.isAuthenticated() &&
user.isInRole('admin') &&
user.hasAuthority('add-admin')) ||
user.hasAuthority('delete-admin')
) {
// Code
}
var result = number1 + number2 + number3 + number4 + number5;
// bad
if (
(user.isAuthenticated() &&
user.isInRole('admin') &&
user.hasAuthority('add-admin')) ||
user.hasAuthority('delete-admin')
) {
// Code
}
var result = number1 + number2 + number3 + number4 + number5
- [强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在
,
或;
前换行。
示例:
// good
var obj = {
a: 1,
b: 2,
c: 3,
};
foo(aVeryVeryLongArgument, anotherVeryLongArgument, callback);
// bad
var obj = {
a: 1,
b: 2,
c: 3,
};
foo(aVeryVeryLongArgument, anotherVeryLongArgument, callback)
- [建议] 不同行为或逻辑的语句集,使用空行隔开,更易阅读。
示例:
// 仅为按逻辑换行的示例,不代表setStyle的最优实现
function setStyle(element, property, value) {
if (element == null) {
return
}
element.style[property] = value
}
- [建议] 在语句的行长度超过
120
时,根据逻辑条件合理缩进。
示例:
// 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。
// 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 if 内语句块能容易视觉辨识。
if (
(user.isAuthenticated() &&
user.isInRole('admin') &&
user.hasAuthority('add-admin')) ||
user.hasAuthority('delete-admin')
) {
// Code
}
// 按一定长度截断字符串,并使用 + 运算符进行连接。
// 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。
// 特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。
var html =
'' + // 此处用一个空字符串,以便整个HTML片段都在新行严格对齐
'<article>' +
'<h1>Title here</h1>' +
'<p>This is a paragraph</p>' +
'<footer>Complete</footer>' +
'</article>';
// 也可使用数组来进行拼接,相对 + 更容易调整缩进。
var html = [
'<article>',
'<h1>Title here</h1>',
'<p>This is a paragraph</p>',
'<footer>Complete</footer>',
'</article>',
];
html = html.join('');
// 当参数过多时,将每个参数独立写在一行上,并将结束的右括号 ) 独立一行。
// 所有参数必须增加一个缩进。
foo(aVeryVeryLongArgument, anotherVeryLongArgument, callback);
// 也可以按逻辑对参数进行组合。
// 最经典的是baidu.format函数,调用时将参数分为“模板”和“数据”两块
baidu.format(dateFormatTemplate, year, month, date, hour, minute, second);
// 当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。
// 这通常出现在匿名函数或者对象初始化等作为参数时,如setTimeout函数等。
setTimeout(function () {
alert('hello')
}, 200);
order.data.read(
'id=' + me.model.id,
function (data) {
me.attchToModel(data.result);
callback()
},
300
);
// 链式调用较长时采用缩进进行调整。
$('#items').find('.selected').highlight().end();
// 三元运算符由3部分组成,因此其换行应当根据每个部分的长度不同,形成不同的情况。
var result = thisIsAVeryVeryLongCondition ? resultA : resultB;
var result = condition ? thisIsAVeryVeryLongResult : resultB;
// 数组和对象初始化的混用,严格按照每个对象的 { 和结束 } 在独立一行的风格书写。
var array = [
{
// ...
},
{
// ...
},
]
- [建议] 对于
if...else...
、try...catch...finally
等语句,推荐使用在}
号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
示例:
if (condition) {
// some statements;
} else {
// some statements;
}
try {
// some statements;
} catch (ex) {
// some statements;
}
# 3.5 引用
const
和 let
都是块级作用域,var
是函数级作用域
对所有引用都使用 const
,不要使用 var
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2
如果引用是可变动的,则使用 let
// bad
var count = 1;
if (count < 10) {
count += 1
}
// good
let count = 1;
if (count < 10) {
count += 1
}
# 3.6 对象
请使用字面量值创建对象
// bad const a = new Object{} // good const a = {}
别使用保留字作为对象的键值,这样在 IE8 下不会运行
// bad const a = { default: {}, // default 是保留字 common: {} } // good const a = { defaults: {}, common: {} }
请使用对象方法的简写方式
// bad const item = { value: 1, addValue: function (val) { return item.value + val } } // good const item = { value: 1, addValue(val) { return item.value + val } }
请使用对象属性值的简写方式
const job = 'FrontEnd' // bad const item = { job: job } // good const item = { job }
对象属性值的简写方式要和声明式的方式分组
const job = 'FrontEnd' const department = 'JDC' // bad const item = { sex: 'male', job, age: 25, department } // good const item = { job, department, sex: 'male', age: 25 }
# 3.7 对象
数组
请使用字面量值创建数组
// bad const items = new Array() // good const items = []
向数组中添加元素时,请使用
push
方法const items = [] // bad items[items.length] = 'test' // good items.push('test')
使用拓展运算符
...
复制数组// bad const items = [] const itemsCopy = [] const len = items.length let i // bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i] } // good itemsCopy = [...items]
使用数组的
map
等方法时,请使用return
声明,如果是单一声明语句的情况,可 省略return
// good [1, 2, 3] .map((x) => { const y = x + 1; return x * y }) [ // good (1, 2, 3) ].map((x) => x + 1); // bad const flat = {}[([0, 1], [2, 3], [4, 5])].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[index] = flatten }); // good const flat = {}[([0, 1], [2, 3], [4, 5])].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[index] = flatten; return flatten }); // bad inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee' } else { return false } }); // good inbox.filter((msg) => { const { subject, author } = msg; if (subject === 'Mockingbird') { return author === 'Harper Lee' } return false })
# 3.8 对象解构赋值
当需要使用对象的多个属性时,请使用解构赋值
// bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}` } // good function getFullName(user) { const { firstName, lastName } = user; return `${firstName} ${lastName}` } // better function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}` }
当需要使用数组的多个值时,请同样使用解构赋值
const arr = [1, 2, 3, 4] // bad const first = arr[0] const second = arr[1] // good const [first, second] = arr
函数需要回传多个值时,请使用对象的解构,而不是数组的解构
// bad function doSomething() { return [top, right, bottom, left] } // 如果是数组解构,那么在调用时就需要考虑数据的顺序 const [top, xx, xxx, left] = doSomething(); // good function doSomething() { return { top, right, bottom, left } } // 此时不需要考虑数据的顺序 const { top, left } = doSomething()
# 3.9 字符串
字符串统一使用单引号的形式
''
// bad const department = 'JDC'; // good const department = 'JDC'
字符串太长的时候,请不要使用字符串连接符换行
\
,而是使用+
const str = '凹凸实验室 凹凸实验室 凹凸实验室' + '凹凸实验室 凹凸实验室 凹凸实验室' + '凹凸实验室 凹凸实验室'
程序化生成字符串时,请使用模板字符串
const test = 'test'; // bad const str = ['a', 'b', test].join(); // bad const str = 'a' + 'b' + test; // good const str = `ab${test}`
# 3.10 函数
请使用函数声明,而不是函数表达式
// bad const foo = function () { // do something }; // good function foo() { // do something }
不要在非函数代码块中声明函数
// bad if (isUse) { function test() { // do something } } // good let test; if (isUse) { test = () => { // do something } }
不要使用
arguments
,可以选择使用...
arguments
只是一个类数组,而...
是一个真正的数组// bad function test() { const args = Array.prototype.slice.call(arguments); return args.join('') } // good function test(...args) { return args.join('') }
不要更改函数参数的值
// bad function test (opts) { opts = opts || {} } // good function test (opts = {}) { // ... }
# 3.11 原型
使用
class
,避免直接操作prototype
// bad function Queue (contents = []) { this._queue = [..contents] } Queue.prototype.pop = function () { const value = this._queue[0] this._queue.splice(0, 1) return value } // good class Queue { constructor (contents = []) { this._queue = [...contents] } pop () { const value = this._queue[0] this._queue.splice(0, 1) return value } }
# 3.12 模块
使用标准的 ES6 模块语法
import
和export
// bad const util = require('./util') module.exports = util // good import Util from './util' export default Util // better import { Util } from './util' export default Util
不要使用
import
的通配符*
,这样可以确保你只有一个默认的 export// bad import * as Util from './util' // good import Util from './util'
# 3.13 迭代器
不要使用
iterators
const numbers = [1, 2, 3, 4, 5] // bad let sum = 0 for (let num of numbers) { sum += num } // good let sum = 0 numbers.forEach(num => sum += num) // better const sum = numbers.reduce((total, num) => total + num, 0)
# 3.14 对象属性
使用
.
来访问对象属性const joke = { name: 'haha', age: 28 } // bad const name = joke['name'] // good const name = joke.name
# 3.15 命名法
- [强制]
变量
使用Camel命名法
。
示例:
var loadingModules = {}
- [强制]
常量
使用全部字母大写,单词间下划线分隔
的命名方式。
示例:
var HTML_ENTITY = {}
- [强制]
函数
使用Camel命名法
。
示例:
function stringFormat(source) {}
- [强制] 函数的
参数
使用Camel命名法
。
示例:
function hear(theBells) {}
- [强制]
类
使用Pascal命名法
。
示例:
function TextNode(options) {}
- [强制] 类的
方法 / 属性
使用Camel命名法
。
示例:
function TextNode(value, engine) {
this.value = value;
this.engine = engine
}
TextNode.prototype.clone = function () {
return this
}
- [强制]
枚举变量
使用Pascal命名法
,枚举的属性
使用全部字母大写,单词间下划线分隔
的命名方式。
示例:
var TargetState = {
READING: 1,
READED: 2,
APPLIED: 3,
READY: 4,
}
- [强制]
命名空间
使用Camel命名法
。
示例:
equipments.heavyWeapons = {}
- [强制] 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小 写保持一致。
示例:
function XMLParser() {}
function insertHTML(element, html) {}
var httpRequest = new HTTPRequest()
- [强制]
类名
使用名词
。
示例:
function Engine(options) {}
- [建议]
函数名
使用动宾短语
。
示例:
function getStyle(element) {}
- [建议]
boolean
类型的变量使用is
或has
开头。
示例:
var isReady = false;
var hasMoreCommands = false
- [建议]
Promise对象
用动宾短语的进行时
表达。
示例:
var loadingData = ajax.get('url');
loadingData.then(callback)
# 3.16 变量声明
声明变量时,请使用
const
、let
关键字,如果没有写关键字,变量就会暴露在全局 上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这 里推荐使用const
来声明变量,我们需要避免全局命名空间的污染。// bad demo = new Demo() // good const demo = new Demo()
将所有的
const
和let
分组// bad let a const b let c const d let e // good const b const d let a let c let e
# 4 注释规则
# 4.1 单行注释
- 两个斜杠 // 可以创建一个单行注释,斜杠后面要增加一个空格,紧接着是注释内容。
- 注释的缩进需要与所注释的代码一致,且要位于被注释代码的上面。
代码演示如下:
// w3cschool教程测试函数
function func() {
// 用来存储定时器函数标识
let flag = null;
}
# 4.2 多行注释
// 可以创建多行注释,也就是以 "/" 开始,"/" 结束,中间是注释内容。
既然是多行注释,自然被注释的内容是可以换行的。
尽量使用单行注释替代多行注释,如果注释函数,推荐使用多行注释。
# 4.3 函数的注释
函数是使用最为频繁的语法结构,相对较为复杂,所以良好的注释对于理解函数的功能非常 有必要。
注释格式如下:
/*方法说明
*@method 方法名
*@for 所属类名
*@param{参数类型}参数名 参数说明
*@return {返回值类型} 返回值说明
*/
可以看到在注释的开始于结尾分别是 / 与 /,具体的注释内容前面也带有一个星号,看起 来更加整齐。
看一段简单的注释代码实例:
/*函数说明
* @param {string} p1 参数1的说明
* @param {string} p2 参数2的说明,比较长
* 那就换行了.
* @param {number=} p3 参数3的说明(可选)
* @return {Object} 返回值描述
*/
function foo(p1, p2, p3) {
var p3 = p3 || 10;
return {
p1: p1,
p2: p2,
p3: p3
};
}
# 4.4 模块注释
模块注释格式如下:
/* 模块说明
* @module 模块名
*/
/* 类说明
* @class 类名
* @constructor
*/
由于类分为静态类与非静态类,所以 class 需要与 constructor 或者 static 配合使用。
# 4.5 注释内容
知道为什么需要注释,那么也就知道注释应该怎么写。
注释的目的是告诉阅读者不宜察觉或者不易获取到的信息,而不是一目了然的东西。
// 声明一个变量timer
let timer=null;
# 4.6 文件注释
- [强制] 文件顶部必须包含文件注释,用
@file
标识文件说明。
示例:
/**
* @file Describe the file
*/
- [建议] 文件注释中可以用
@author
标识开发者信息。
解释:
开发者信息能够体现开发人员对文件的贡献,并且能够让遇到问题或希望了解相关信息的人
找到维护人。通常情况文件在被创建时标识的是创建者。随着项目的进展,越来越多的人加
入,参与这个文件的开发,新的作者应该被加入 @author
标识。
@author
标识具有多人时,原则是按照 责任
进行排序。通常的说就是如果有问题,就
是找第一个人应该比找第二个人有效。比如文件的创建者由于各种原因,模块移交给了其他
人或其他团队,后来因为新增需求,其他人在新增代码时,添加 @author
标识应该把自
己的名字添加在创建人的前面。
@author
中的名字不允许被删除。任何劳动成果都应该被尊重。
业务项目中,一个文件可能被多人频繁修改,并且每个人的维护时间都可能不会很长,不建
议为文件增加 @author
标识。通过版本控制系统追踪变更,按业务逻辑单元确定模块的
维护责任人,通过文档与 wiki 跟踪和查询,是更好的责任管理方式。
对于业务逻辑无关的技术型基础项目,特别是开源的公共项目,应使用 @author
标识。
示例:
/**
* @file Describe the file
* @author author-name(mail-name@domain.com)
* author-name2(mail-name2@domain.com)
*/
# 4.7 命名空间注释
- [建议] 命名空间使用
@namespace
标识。
示例:
/**
* @namespace
*/
var util = {}
# 4.8 类注释
- [建议] 使用
@class
标记类或构造函数。
解释:
对于使用对象 constructor
属性来定义的构造函数,可以使用 @constructor
来标记
。
示例:
/**
* 描述
*
* @class
*/
function Developer() {
// constructor body
}
- [建议] 使用
@extends
标记类的继承信息。
示例:
/**
* 描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this)
// constructor body
}
util.inherits(Fronteer, Developer)
- [强制] 使用包装方式扩展类成员时, 必须通过
@lends
进行重新指向。
解释:
没有 @lends
标记将无法为该类生成包含扩展类成员的文档。
示例:
/**
* 类描述
*
* @class
* @extends Developer
*/
function Fronteer() {
Developer.call(this)
// constructor body
}
util.extend(
Fronteer.prototype,
/** @lends Fronteer.prototype */ {
_getLevel: function () {
// TODO
},
}
)
- [强制] 类的属性或方法等成员信息使用
@public
/@protected
/@private
中的任意一个,指明可访问性。
解释:
生成的文档中将有可访问性的标记,避免用户直接使用非 public
的属性或方法。
示例:
/**
* 类描述
*
* @class
* @extends Developer
*/
var Fronteer = function () {
Developer.call(this);
/**
* 属性描述
*
* @type {string}
* @private
*/
this._level = 'T12'
// constructor body
};
util.inherits(Fronteer, Developer);
/**
* 方法描述
*
* @private
* @return {string} 返回值描述
*/
Fronteer.prototype._getLevel = function () {}
# 4.9 事件注释
/**
* 点击处理
*
* @fires Select#change
* @private
*/
Select.prototype.clickHandler = function () {
/**
* 值变更时触发
*
* @event Select#change
* @param {Object} e e描述
* @param {string} e.before before描述
* @param {string} e.after after描述
*/
this.fire('change', {
before: 'foo',
after: 'bar',
})
}
- [强制] 常量必须使用
@const
标记,并包含说明和类型信息。
示例:
/**
* 常量说明
*
* @const
* @type {string}
*/
var REQUEST_URL = 'myurl.do'
# 4.10 复杂类型注释
- [建议] 对于类型未定义的复杂结构的注释,可以使用
@typedef
标识来定义。
示例:
// `namespaceA~` 可以换成其它 namepaths 前缀,目的是为了生成文档中能显示 `@typedef` 定义的类型和链接。
/**
* 服务器
*
* @typedef {Object} namespaceA~Server
* @property {string} host 主机
* @property {number} port 端口
*/
/**
* 服务器列表
*
* @type {Array.<namespaceA~Server>}
*/
var servers = [
{
host: '1.2.3.4',
port: 8080,
},
{
host: '1.2.3.5',
port: 8081,
},
]
# 4.11 AMD 模块注释
- [强制] AMD 模块使用
@module
或@exports
标识。
解释:
@exports 与 @module 都可以用来标识模块,区别在于 @module 可以省略模块名称。而只 使用 @exports 时在 namepaths 中可以省略 module: 前缀。
示例:
define(function (require) {
/**
* foo description
*
* @exports Foo
*/
var foo = {
// TODO
};
/**
* baz description
*
* @return {boolean} return description
*/
foo.baz = function () {
// TODO
};
return foo
})
也可以在 exports 变量前使用 @module 标识:
define(function (require) {
/**
* module description.
*
* @module foo
*/
var exports = {};
/**
* bar description
*
*/
exports.bar = function () {
// TODO
};
return exports
})
如果直接使用 factory 的 exports 参数,还可以:
/**
* module description.
*
* @module
*/
define(function (require, exports) {
/**
* bar description
*
*/
exports.bar = function () {
// TODO
};
return exports
})
- [强制] 对于已使用
@module
标识为 AMD 模块 的引用,在namepaths
中必须增加module:
作前缀。
解释:
namepaths 没有 module: 前缀时,生成的文档中将无法正确生成链接。
示例:
/**
* 点击处理
*
* @fires module:Select#change
* @private
*/
Select.prototype.clickHandler = function () {
/**
* 值变更时触发
*
* @event module:Select#change
* @param {Object} e e描述
* @param {string} e.before before描述
* @param {string} e.after after描述
*/
this.fire('change', {
before: 'foo',
after: 'bar',
})
}
- [建议] 对于类定义的模块,可以使用
@alias
标识构建函数。
示例:
/**
* A module representing a jacket.
* @module jacket
*/
define(function () {
/**
* @class
* @alias module:jacket
*/
var Jacket = function () {};
return Jacket
})
- [建议] 多模块定义时,可以使用
@exports
标识各个模块。
示例:
// one module
define(
'html/utils',
/**
* Utility functions to ease working with DOM elements.
* @exports html/utils
*/
function () {
var exports = {};
return exports
}
);
// another module
define(
'tag',
/** @exports tag */
function () {
var exports = {};
return exports
}
)
- [建议] 对于 exports 为 Object 的模块,可以使用
@namespace
标识。
解释:
使用 @namespace 而不是 @module 或 @exports 时,对模块的引用可以省略 module: 前缀 。
- [建议] 对于 exports 为类名的模块,使用
@class
和@exports
标识。
示例:
// 只使用 @class Bar 时,类方法和属性都必须增加 @name Bar#methodName 来标识,与 @exports 配合可以免除这一麻烦,并且在引用时可以省去 module: 前缀。
// 另外需要注意类名需要使用 var 定义的方式。
/**
* Bar description
*
* @see foo
* @exports Bar
* @class
*/
var Bar = function () {
// TODO
};
/**
* baz description
*
* @return {(string|Array)} return description
*/
Bar.prototype.baz = function () {
// TODO
}
# 4.12 细节注释
对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。
- [建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。
示例:
function foo(p1, p2, opt_p3) {
// 这里对具体内部逻辑进行说明
// 说明太长需要换行
for (...) {
....
}
}
- [强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:
解释:
- TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
- FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对 如何修正进行简单说明。
- HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路 或诡异手段进行描述。
- XXX: 该处存在陷阱。此时需要对陷阱进行描述。
# 5 Hoisting
var
存在变量提升的情况,即var
声明会被提升至该作用域的顶部,但是他们的赋 值并不会。而const
和let
并不存在这种情况,他们被赋予了 Temporal Dead Zones, TDZ (opens new window)function example () { console.log(notDefined) // => throws a ReferenceError } function example () { console.log(declareButNotAssigned) // => undefined var declaredButNotAssigned = true } function example () { let declaredButNotAssigned console.log(declaredButNotAssigned) // => undefined declaredButNotAssigned = true } function example () { console.log(declaredButNotAssigned) // => throws a ReferenceError console.log(typeof declaredButNotAssigned) // => throws a ReferenceError const declaredButNotAssigned = true }
匿名函数的变量名会提升,但函数内容不会
function example () { console.log(anonymous) // => undefined anonymous() var anonymous = function () { console.log('test') } }
命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会
function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying') } } function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named') } }
# 5.1 分号
我们遵循
Standard
的规范,不使用分号。关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好 的
JS
程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。// bad const test = 'good'; (function () { const str = 'hahaha'; })() // good const test = 'good' ;(() => { const str = 'hahaha' })();
# 5.2 标准特性
为了代码的可移植性和兼容性,我们应该最大化的使用标准方法,例如优先使用
string.charAt(3)
而不是 string[3]
eval()
由于
eval
方法比较evil
,所以我们约定禁止使用该方法with() {}
由于
with
方法会产生神奇的作用域,所以我们也是禁止使用该方法的for-in 循环
推荐使用
for in
语法,但是在对对象进行操作时,容易忘了检测hasOwnProperty(key)
,所以我们启用了ESLint
的guard-for-in
选项对数组进行
for in
的时候,顺序是不固定的修改内置对象的原型
不要修改内置对象,如
Object
和Array
# vue项目规范
# 1 版本规范
名称 | 版本号 |
---|---|
node | 14.9.0 |
npm | 6.14.8 |
vue | ^2.6.11 |
vuex | ^3.4.0 |
vue-router | ^3.2.0 |
element-ui | ^2.12.0 |
eslint | ^6.7.2 |
# 2 Vue 编码规范
vue 项目规范以 Vue 官方规范 (https://cn.vuejs.org/v2/style-guide/) 中的 A 规范为基础,在 其上面进行项目开发,故所有代码均遵守该规范。 请仔仔细细阅读 Vue 官方规范,切记,此为第一步。
# 2.1.组件规范
- 组件名为多个单词 组件名应该始终是多个单词组成(大于等于 2),且命名规范为 kebab-case 格式。 这样做可以避免跟现有的以及未来的 HTML 元素相冲突,因为所有的 HTML 元素名称都是单个 单词的。
// good
export default {
name: 'TodoItem'
// ...
};
// bad
export default {
name: 'Todo',
// ...
}
export default {
name: 'todo-item',
// ...
}
- 组件文件名为 pascal-case 格式
// good
components/
|- my-component.vue;
// bad
components/
|- myComponent.vue
|- MyComponent.vue
- 基础组件文件名为 base 开头,使用完整单词而不是缩写
// good
components/
|- base-button.vue|- base-table.vue
|- base-icon.vue;
// bad
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue
- 和父组件紧密耦合的子组件应该以父组件名作为前缀命名
// good
components/
|- todo-list.vue
|- todo-list-item.vue
|- todo-list-item-button.vue
|- user-profile-options.vue; (完整单词;)
// bad
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
|- UProfOpts.vue; (使用了缩写;)
- 在 Template 模版中使用组件,应使用 PascalCase 模式,并且使用自闭合组件
// good
<!-- 在单文件组件、字符串模板和 JSX 中 -->
<MyComponent />
<Row><table :column="data"/></Row>
// bad
<my-component /> <row><table :column="data"/></row>
- 组件的 data 必须是一个函数 当在组件中使用 data 属性的时候(除了 new Vue 外的任何地方),它的值必须是返回一个对 象的函数。 因为如果直接是一个对象的话,子组件之间的属性值会互相影响。
// good
export default {
data () {
return {
name: 'jack'
}
}
}
// bad
export default {
data: {
name: 'jack'
}
}
- Prop 定义应该尽量详细
- 必须使用 camelCase 驼峰命名
- 必须指定类型
- 必须加上注释,表明其含义
- 必须加上 required 或者 default,两者二选其一
- 如果有业务需要,必须加上 validator 验证
// good
{
// 组件状态,用于控制组件的颜色
{
String,
required;: true,
validator;: function (value) {
return [
'succ',
'info',
'error'
].indexOf(value) !== -1
}
},
// 用户级别,用于显示皇冠个数
userLevel;:{
String,required;: true
}
}
- 为组件样式设置作用域
// good
<template>
<button; class="btn btn-close">X</button>
</template>
<!-- 使用 `scoped` 特性 -->
<style; scoped>;
.btn-close; {
background-color;: red;
}
</style>
// bad
<template>
<button; class="btn btn-close">X</button>
</template>
<!-- 没有使用 `scoped` 特性 -->
<style>;
.btn-close; {
background-color;: red;
}
</style>
- 如果特性元素较多,应该主动换行
// good
<MyComponent; foo="a"; bar="b"; baz="c";
foo="a"; bar="b"; baz="c";
foo="a"; bar="b"; baz="c"
/>
// bad
<MyComponent; foo="a"; bar="b"; baz="c"; foo="a"; bar="b"; baz="c"; foo="a"; bar="b"; baz="c"; foo="a";
bar="b"; baz="c"/>
# 2.2.模板中使用简单的表达式
- 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。 复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而 且计算属性和方法使得代码可以重用。
// good
<template>
<p>{;{ normalizedFullName }}</p>
</template>;;;;;;;;;;;;;
// 复杂表达式已经移入一个计算属性
{
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
// bad
<template>
<p>
{;{
fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}}
</p>
</template>
# 2.3.指令都使用缩写形式
- 指令推荐都使用缩写形式,(用 : 表示 v-bind: 、用 @ 表示 v-on: 和用 # 表示 v-slot:)
// good
<input;
@input="onInput";
@focus="onFocus"
>
// bad
<input;
v-on;:input="onInput";
@focus="onFocus"
>
# 2.4.标签顺序保持一致
单文件组件应该总是让标签顺序保持为
// good
<template>;...</template>
<script>;...</script>
<style>;...</style>
// bad
<template>;...</template>
<style>;...</style>
<script>;...</script>
# 2.5.必须为 v-for 设置键值 key
# 2.6.v-show 与 v-if 选择
如果运行时,需要非常频繁地切换,使用 v-show ;如果在运行时,条件很少改变,使用 v-if。
# 2.7.script 标签内部结构顺序
name > components > mixins > props > data > computed > watch > filter > 钩子函数(钩子函数按其 执行顺序) > methods
# 2.8. Vue Router 规范
- 页面跳转数据传递使用路由参数 页面跳转,例如 A 页面跳转到 B 页面,需要将 A 页面的数据传递到 B 页面,推荐使用 路由 参数进行传参,而不是将需要传递的数据保存 vuex,然后在 B 页面取出 vuex 的数据,因为如 果在 B 页面刷新会导致 vuex 数据丢失,导致 B 页面无法正常显示数据。
// good
let id = ' 123';
this.$router.push({ name: 'userCenter', query: { id: id } });
- 使用路由懒加载(延迟加载)机制
{
'/uploadAttachment',
name;: 'uploadAttachment',
meta;: {
'上传附件'
},
() => import('@/view/components/uploadAttachment/index.vue')
},
- router 中的命名规范 path、childrenPoints 命名规范采用 kebab-case 命名规范(尽量 vue 文件的目录结构保持一致, 因为目录、文件名都是 kebab-case,这样很方便找到对应的文件) name 命名规范采用 kebab-case 命名规范且和 component 组件名保持一致!(因为要保持 keep-alive 特性,keep-alive 按照 component 的 name 进行缓存,所以两者必须高度保持一致)
// 动态加载
export const reload = [{
path: '/reload',
name: 'reload',
component: Main,
meta: {
title: '动态加载',
icon: 'icon iconfont'
},
children: [{
path: '/reload/smart-reload-list',
name: 'SmartReloadList',
meta: {
title: 'SmartReload',
childrenPoints: [{
title: '查询',
name: 'smart-reload-search'
},
{
title: '执行 reload',
name: 'smart-reload-update'
},
{
title: '查看执行结果',
name: 'smart-reload-result'
}]
},
component: () = >import('@/views/reload/smart-reload/smart-reload-list.vue')
}];
}]
- router 中的 path 命名规范 path 除了采用 kebab-case 命名规范以外,必须以 / 开头,即使是 children 里的 path 也要以 / 开头。 目的: 经常有这样的场景:某个页面有问题,要立刻找到这个 vue 文件,如果不用以/开头,path 为 parent 和 children 组成的,可能经常需要在 router 文件里搜索多次才能找到,而如果以/开头,则能立刻搜索到对应的组件。
{
'/file',
name;: 'File',
component;: Main,
meta;: {
'文件服务',
icon;: 'ios-cloud-upload'
},
[{
path: '/file/file-list',
name: 'FileList',
component: () = >import('@/views/file/file-list.vue')
},
{
'/file/file-add',
name;: 'FileAdd',
component;: () =; >import('@/views/file/file-add.vue')
},
{
'/file/file-update',
name;: 'FileUpdate',
component;: () =; >import('@/views/file/file-update.vue')
}]
}
# 3.Vue 项目目录规范
# 3.1.基础
vue 项目中的所有命名一定要与后端命名统一。 比如权限:后端 privilege, 前端无论 router , store, api 等都必须使用 privielege 单词!
# 3.2.使用 Vue-cli 脚手架
使用 vue-cli2 来初始化项目,项目名按照上面的命名规范。
# 3.3.目录说明
目录名按照上面的命名规范,其中 components 组件用大写驼峰,其余除 components 组件目 录外的所有目录均使用 kebab-case 命名。
src 源码目录
|-- api 所有 api 接口
|-- assets 静态资源,images, icons, styles 等
|-- components 公用组件
|-- config 配置信息
|-- constants 常量信息,项目所有 Enum, 全局常量等
|-- directives 自定义指令
|-- filters 过滤器,全局工具
|-- datas 模拟数据,临时存放
|-- lib 外部引用的插件存放及修改文件
|-- mock 模拟接口,临时存放
|-- plugins 插件,全局使用
|-- router 路由,统一管理
|-- store vuex, 统一管理
|-- themes 自定义样式主题
|-- views 视图目录
| |-- role role 模块名
| |-- |-- role-list.vue role 列表页面
| |-- |-- role-add.vue role 新建页面
| |-- |-- role-update.vue role 更新页面
| |-- |-- index.less role 模块样式
| |-- |-- components role 模块通用组件文件夹
| |-- employee employee 模块
- api 目录
- 文件、变量命名要与后端保持一致。
- 此目录对应后端 API 接口,按照后端一个 controller 一个 api.js 文件。若项目较大时,可 以按照业务划分子目录,并与后端保持一致。
- api 中的方法名字要与后端 api url 尽量保持语义高度一致性。
- 对于 api 中的每个方法要添加注释,注释与后端 swagger 文档保持一致。
// good
后端; url;: EmployeeController.java
/employee/add
/employee/delete/{id} ;;;;;;;;;;;;;
/employee/u;pdate;
前端;: employee.js;
// 添加员工
function addEmployee(data) {
return postAxios('/employee/add', data)
}
// 更新员工信息
function updateEmployee(data) {
return postAxios('/employee/update', data)
}
// 删除员工
function deleteEmployee(employeeId) {
return postAxios('/employee/delete/' + employeeId)
}
// 获取员工信息
function getEmployee(data) {
return postAxios('/employee/get/', data)
}
// 获取员工信息列表
function getEmployeeList(data) {
return postAxios('/employee/getlist/', data)
}
- assets 目录 assets 为静态资源,里面存放 images, styles, icons 等静态资源,静态资源命名格式为 kebab-case
|assets
|-- icons
|-- images
| |-- background-color.png
| |-- upload-header.png
|-- styles
- components 目录 此目录应按照组件进行目录划分,目录命名为 kebab-case,组件命名规则也为 kebab-case
|components
|-- error-log
| |-- index.vue
| |-- index.less
|-- markdown-editor
| |-- index.vue
| |-- index.js
|-- kebab-case
- constants 目录 此 目 录 存 放 项 目 所 有 常 量 , 如 果 常 量 在 vue 中 使 用 , 请 使 用 vue-enum 插 件 (https://www.npmjs.com/package/vue-enum)
目录结构;:
|constants
|-- index.js
|-- role.js
|-- employee.js;
例子;: employee.js;
export const EMPLOYEE_STATUS = {
NORMAL: {
value: 1,
desc: '正常'
},
DISABLED: {
value: 1,
desc: '禁用'
},
DELETED: {
value: 2,
desc: '已删除'
}
};
export const EMPLOYEE_ACCOUNT_TYPE = {
QQ: {
value: 1,
desc: 'QQ 登录'
},
WECHAT: {
value: 2,
desc: '微信登录'
},
DINGDING: {
value: 3,
desc: '钉钉登录'
},
USERNAME: {
value: 4,
desc: '用户名密码登录'
}
};
export default {
EMPLOYEE_STATUS,
EMPLOYEE_ACCOUNT_TYPE
};
- router 与 store 目录 这两个目录一定要将业务进行拆分,不能放到一个 js 文件里。 router 尽量按照 views 中的结构保持一致 store 按照业务进行拆分不同的 js 文件
- views 目录 命名要与后端、router、api 等保持一致components 中组件要使用 PascalCase 规则
|-- views 视图目录
| |-- role role 模块名
| | |-- role-list.vue role 列表页面
| | |-- role-add.vue role 新建页面
| | |-- role-update.vue role 更新页面
| | |-- index.less role 模块样式
| | |-- components role 模块通用组件文件夹
| | | |-- role-header.vue role 头部组件
| | | |-- role-modal.vue role 弹出框组件
| |-- employee employee 模块
| |-- behavior-log 行为日志 log 模块
| |-- code-generator 代码生成器模块
# 3.4.注释说明
整理必须加注释的地方 1. 公共组件使用说明 2. api 目录的接口 js 文件必须加注释 3. store 中的 state, mutation, action 等必须加注释 4. vue 文件中的 template 必须加注释,若文件较大添加 start end 注释 5. vue 文件的 methods,每个 method 必须添加注释 6. vue 文件的 data, 非常见单词要加注释
# 3.5.其他
- 尽量不要手动操作 DOM 因使用 vue 框架,所以在项目开发中尽量使用 vue 的数据驱动更新 DOM,尽量(不到万不得 已)不要手动操作 DOM,包括:增删改 dom 元素、以及更改样式、添加事件等。
- 删除无用代码 因使用了 git/svn 等代码版本工具,对于无用代码必须及时删除,例如:一些调试的 console 语 句、无用的弃用功能代码。
← hos-app引用说明 后端规范 →