前言
相信项目开发中, 很多同学会遇到类似情况, 团队中开发人员彼此很难阅读对方的代码, 需要猜测代码的含义,因此协作开发效率大打折扣, 后期的代码维护更是噩梦,造成此情形的很大原因之一, 可能是没有一套共同遵循的规范, 较大项目中, 该情况尤为突出.
原则
1.项目中约定一套规范, 一直遵循下去;
2.要以实用为标准, 在不影响浏览器解析和代码执行的前提下, 可以不必要处处遵循;
3.不具有强制性, 因为它并不是语法层面的规范, 而是大家约定俗成的最佳实践;
4. 规范要与时俱进,随着时间的推移,技术的更新,未必是将来的最佳实践;
HTML规范
1. 代码风格
1.1 缩进
统一两个空格缩进
1.2 命名
-
class必须单词全部字母小写,单词间以
-分隔 -
class必须代表相应模块或部件的内容或功能,不得以样式信息进行命名
-
元素ID必须保证页面唯一
同一个页面中,不同的元素包含相同的 id,不符合 id 的属性含义。并且使用 document.getElementById 时可能导致难以追查的问题。 -
ID必须使用驼峰命名法
-
ID,class命名,在避免冲突并描述清楚的前提下尽可能短
-
同一页面,应避免使用相同的
name与ID
IE 浏览器会混淆元素的 id 和 name 属性, document.getElementById 可能获得不期望的元素。所以在对元素的 id 与 name 属性的命名需要非常小心。
1.3 标签
标签名必须使用小写字母
<p>Hello world!</p>
对于无需自闭和的标签,不允许自闭和
常见无需自闭和的标签有
input、br、img、等
<input type="text" name="title">
除HTML5 中规定允许省略的闭合标签外,其他不允许省略闭合标签。(对代码体积要求非常严苛的场景,可以例外)
<!-- good -->
<ul>
<li>first</li>
<li>second</li>
</ul>
<!-- bad -->
<ul>
<li>first
<li>second
</ul>
标签使用必须符合标签嵌套规则
比如:div不得置于p中,tbody必须置于table中
在css可以实现相同需求的情况下尽量不使用表格进行布局
在兼容性允许的情况下应尽量保持语义正确性。对网格对齐和拉伸性有严格要求的场景允许例外,如多列复杂表单。
标签的使用应尽量简洁,减少不必要的标签
<!-- good -->
<img class="avatar" src="image.png">
<!-- bad -->
<span class="avatar">
<img src="image.png">
</span>
1.4 属性
-
属性名必须使用小写字母
......
-
属性值必须使用双引号包围
-
布尔类型的属性,建议不添加属性值
-
自定义属性推荐使用
data-
使用前缀有助于区分自定义属性和标准定义的属性
<ol data-ui-type="Select"></ol>
1.5 语义化
我们一直都在说语义化编程,语义化编程,但是在代码中很少有人完全使用正确的元素。使用语义化标签也是有理由SEO的。
语义化是指:根据元素其被创造出来时的初始意义来使用它。意思就是用正确的标签干正确的事,而不是只有div和span。
2. 通用
2.1 DOCTYPE
-
使用 HTML5 的 doctype 来启用标准模式,建议使用大写的 DOCTYPE。
-
建议在html标签上设置正确的lang属性
有助于提高页面的可访问性,如:让语音合成工具确定其所应该采用的发音,令翻译工具确定其翻译语言等。
<html lang="zh-CN">
2.2 CSS和js引入
-
引入
css时必须指明rel="stylesheet" -
引入
css和js时无须指明type属性 -
样式放置于外部
css中,功能放置于外部js中 -
在
head中引入页面需要的所有css资源
2.3 注释
3. head
3.1 title
- 页面必须包含
title标签声明标题 title必须作为head的直接子元素,并紧随charset声明之后
3.2 favicon
- 保证
favicon可访问
在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 favicon.ico 。为了保证favicon可访问,避免404,必须遵循以下两种方法之一:
1、在 Web Server 根目录放置 favicon.ico 文件。
2、使用 link 指定 favicon。
<link rel="shortcut icon" href="path/to/favicon.ico">
3.3 viewport
-
若页面需要在移动设备上使用,需设置viewport
4. 图片
禁止img的src取值为空,延迟加载的图片也要增加默认的src
src 取值为空,会导致部分浏览器重新加载一次当前页面
避免为img添加不必要的title属性
多余的title影响看图体验,并且增加了页面尺寸
为重要图片添加alt属性。
可以提高图片加载失败时的用户体验
添加width和height属性,以避免页面抖动 有下载需求的图片采用 img 标签实现,无下载需求的图片采用CSS sprite实现。
1、产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 img 形式实现,能方便用户下载。
2、无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 css 雪碧图实现。
5. 表单
5.1 控件标题
有文本标题的控件必须使用 label标签将其与其标题相关联。
有两种方式:
1、将控件置于 label 内。
2、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">
5.2 按钮
使用button元素时必须指明type属性值 button 元素的默认 type 为 submit,如果被置于 form 元素中,点击后将导致表单提交。为显示区分其作用方便理解,必须给出 type 属性。
<button type="submit">提交</button>
<button type="button">取消</button>
尽量不要使用按钮类元素的name属性 由于浏览器兼容性问题,使用按钮的name属性会带来许多难以发现的问题,具体情况可参照w3help.org/zh-cn/cause…
5.3 可访问性
负责主要功能的按钮在
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>
在针对移动设备开发的页面时,根据内容类型指定输入框的 type属性。
<input type="date">
CSS规范
1. 通用规范
文件编码
-
为了避免内容乱码,统一使用
UTF-8编码保存。 -
样式文件第一行设置字符集为 UTF-8
@charset 'UTF-8'; /* 注意字符集说明应在第一行 */
缩进规范
统一使用两个空格缩进
2. 初始化规范
各浏览器厂商的初始样式都不一样,为了消除不同浏览器对 HTML 文本呈现的差异,我们常引入一些初始化样式,如 normalize.css、reset.css 等,当对于这些样式的引入我们需要注意下面几种情况:
-
不使用 UI 框架,由零开始搭建
从零开始搭建的情况下,进行样式初始化,在项目最开始的时候就引入,不要在开发中途引入,避免不可预知的样式冲突。 -
不使用 UI 框架,但使用了部分插件
插件往往都带有自己特有的样式,如富文本插件,在开发中途使用初始化样式有可能导致样式错乱,所以不建议大范围的初始化,只需简单进行初始化即可。- { padding: 0; margin: 0; }
-
已使用 UI 框架
在明确知道需要使用 UI 框架的时候,不使用第三方初始化样式,不管是在项目开始前还是进行中,因为 UI 框架一般都自带初始化,额外引入了反而会影响原有效果。
3. 代码规范
命名规范
class 应以功能或内容命名,不以表现形式命名
class 单词字母小写,多个单词组成时,采用中划线_分隔,如class_nameid采用小驼峰,如idName
使用唯一的 id 作为 Javascript hook, 同时避免创建无样式信息的 class
代码风格
-
统一使用
展开格式,不推荐紧凑格式/* 展开格式 */ .test { color: red; font-size: 12px; } /* 紧凑格式 */ .test{color: red;font-size: 12px;} -
属性声明结尾加分号
-
选择器与左括号之间一个空格,属性冒号后一个空格
/* 推荐 */ .test { color: red; font-size: 12px; } /* 不推荐 */ .test{ color:red; font-size:12px; } -
不要为 0 指明单位
-
颜色值和属性值十六进制数值能用简写的尽量用简写
/* 推荐 */ .test { color: #fff; } /* 不推荐 */ .test { color: #ffffff; } -
清除浮动
当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 clear 或触发 BFC 的方式进行 clearfix。尽量不使用增加空标签的方式。
通过伪类清除浮动,如:
.f-cb:after { display: block; clear: both; visibility: hidden; height: 0; overflow: hidden; content: "."; }触发 BFC 的方式很多,常见的有:
- float 非 none
- position 非 static
- overflow 非 visible
字体规范
-
对外商用网站,不要用
font-face引入微软雅黑字体,避免侵权(包括图片内容) -
需要在 Windows 平台显示的中文内容,其字号应不小于
12px网站上使用 微软雅黑 字体有三种形式:
1、【侵权】图片中使用 微软雅黑 字体,比如网站头图 2、【安全】网站 CSS 用 font-family 声明网站使用 微软雅黑 字体,比如文章标题和正文 3、【侵权】网站通过 font-face 引用 微软雅黑 ,这种方式不常见
选择器规范
在严格遵照BEM(Block Element Modifier)时,建议只使用类选择器,但 BEM 书写麻烦,所以建议如下
- 禁用通用选择器 *
- 不使用无具体语义定义的标签选择器
属性顺序
CSS 属性顺序是 CSS 良好编码风格的一部分,有助于提高代码可读性,便于发现代码问题,有利于团队合作,但在项目中发现部分同学在书写属性顺序时较为随意,想到一个属性就写一个。
建议使用下列顺序进行书写
- 定位属性(display、position、float)
- 尺寸属性(width、height、margin、padding、border)
- 字体属性(font、color、text-align)
- 其他属性(background、cursor、outline)
目的是在浏览代码时,能逐步清晰目标元素的效果。
.test {
display: block;
position: relative;
float: left;
width: 100px;
height: 100px;
margin: 0 10px;
padding: 20px 0;
font-size: 12px;
color: #333;
background: rgba(0, 0, 0, 0.5);
border-radius: 10px;
}
4. 注释规范
单行注释
注释以 /* 开始,以 */ 结束,注释内不能嵌套注释,注释内容前后空一个空格。
/* 推荐的单行注释 */
/*不推荐的单行注释*/
注:在 sass 和 less 等预处理语言上也可以使用双斜线注释,但编译后注释内容不会出现在 css 文件中,所以建议统一使用/* */注释。
模块注释
有时候我们需要对一个模块(一段代码块)进行功能性说明,并希望能明显区分其它代码,我们可以模块注释的方式。
注释以 /* 开始,以 */ 结束,前后空一个空格,第一行填写描述,最后一行填写分割线。
/* 推荐的模块注释
---------------------------------------------------- */
/* 不推荐的模块注释 ---------------------------------------------------- */
文件信息注释
如果需要对一个文件进行功能性说明,方便其他人快速明白该文件的作用,推荐在文件开头(字符集说明下)写入下列注释,注释内容包括文件描述、创建人、创建时间等。
@charset "UTF-8";
/**
* @desc 文件功能描述,方便其他人快速理解
* @author 创建人
* @date 创建时间
*/
5. 覆盖规范
- 尽可能
少用!important - vue 单文件组件统一使用 css/less/sass
scoped - 每个页面/组件需要有一个
全局唯一的标识 id/class,属于它下面的样式都需要加上该唯一标识 - 避免全局修改已有样式,必须具体到页面上(通过权重)
- 禁用全匹配
*选择器(特殊情况除外,如初始化) - vue 单文件组件修改样式不生效可使用
/deep/或>>>
6. 媒体查询
优先 PC 端
默认按最大尺寸进行布局,当尺寸缩小时逐步变成移动端布局
body {
background: gray;
}
@media screen and (max-width: 1366px) {
body {
background: red;
}
}
@media screen and (max-width: 1200px) {
body {
background: yellow;
}
}
@media screen and (max-width: 920px) {
body {
background: green;
}
}
@media screen and (max-width: 768px) {
body {
background: black;
}
}
优先移动端
默认按最小尺寸进行布局,当尺寸放大时逐步变成 PC 端布局
body {
background: gray;
}
@media (min-width: 768px) {
body {
background: red;
}
}
@media (min-width: 920px) {
body {
background: green;
}
}
@media (min-width: 1200px) {
body {
background: yellow;
}
}
@media (min-width: 1366px) {
body {
background: red;
}
}
7. 单位规范
CSS 单位有两种,分别是绝对单位和相对单位。
-
常用绝对单位
- px:像素 (计算机屏幕上的一个点)
- cm:厘米
- in:英寸
- pt:磅 (1 pt 等于 1/72 英寸)
-
常用相对单位
- %:父元素百分比
- vw:视口宽度百分比
- vh:视口高度百分比
- em:当前字体倍数
- rem:根元素字体倍数
*rpx:微信小程序专用,规定屏幕宽为 750rpx
使用较多的单位有 px、%、rem 三种,建议 PC 端用 px 单位、移动端用 rem,需要具体控制尺寸还是使用 px
备注:如果需要计算不同单位下的值,可以使用 css3 方法 calc()_
8. 兼容性规范
私有属性的使用
正是由于浏览器厂商的不同,导致了一些样式需要加前缀才生效,下面的常见的浏览器内核和前缀
浏览器
内核
前缀
Firefox
Gecko
-moz-
Chrome
WebKit
-webkit-
IE
Trident
-ms-
Safari
WebKit
-webkit-
Opera
Presto
-o-
CSS3 浏览器私有前缀在前,标准前缀在后
.test {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
-o-border-radius: 10px;
-ms-border-radius: 10px;
border-radius: 10px;
}
备注:在 webpack 环境下,可以使用 postcss-loader 自动添加私有前缀_
Javascript规范
通用规范
文件编码
为了避免内容乱码,统一使用 UTF-8 编码保存。
在文件结尾处,保留一个空行。
代码检测
开启 eslint 代码规范和错误检查。
类型规范
-
js 数据类型有 string、number、boolean、null、undefined、array、function 和 object 这几种,不同数据类型有不同的存储方式,也对应有不用的使用方法,对于数据赋值要注意以下几点
- 初始值类型要明确
- 不要随意变换类型
-
类型检测
优先使用 typeof。对象类型检测使用instanceof。null 或 undefined 的检测使用 == null。 -
字符串开头和结束使用单引号
'...string...'
命名规范
-
变量命名采用小驼峰命名,如:addUser password studentID
-
常量命名采用单词所有字母大写,并用下划线分隔,如:FORM_NAME
-
对于对象、函数、和实例采用小驼峰(camelCase)命名法
// 对象 let isObject = {}; // 函数 function isFun(){ ... }; // 实例 let myBbj = new Object(); -
对于类命名或者构造函数,采用大驼峰命名 User() DateBase()
// 类 class Point { ... }; // 构造函数 function User(options) { this.name = options.name; } let myBbj = new User({ name: 'yup' }); 复制代码
代码规范
缩进
统一使用两个空格缩进,不推荐使用 tap 缩进。
引号
统一使用单引号。
换行
每个独立语句结束后必须换行。
分号
不得省略语句结束的分号
代码块
使用花括号包裹所有的多行代码块。
单行 if 语句也必须使用花括号括住
// 推荐
if (true) {
// TODO ...
}
// 不推荐
if (true) // TODO ...
使用全等符号
在等号表达式中使用类型严格的 ===和!==。使用 === 可以避免等于判断中隐式的类型转换。
// 推荐
if (age === 30) {
// ......
}
// 不推荐
if (age == 30) {
// ......
}
注释规范
单行注释
使用 // 作为单行注释。在评论对象上面另起一行使用单行注释。在注释内容前插入一个空格。
// 单行注释
多行注释
以/*开头,*/结尾,注释内容前后加一个空格
/*
* 第一行注释
* 第二行注释
*/
/* 另外一种写法 */
方法注释
函数(方法)注释也是多行注释的一种,但是包含了特殊的注释要求,关键方法必须加注释。
/**
* 方法功能描述
* @param {*} 参数
* @param {*} 参数
* @param {*} 参数
* @param {*} 参数
* @return 返回值
*/
TODO 注释
使用 // TODO: 标注问题的解决方式。
function Calculator() {
// TODO: total should be configurable by an options param
}
vue项目规范
1. vue 文件
-
基本结构
<template> <div></div> </template> <script> export default { components: {}, data() { return {}; }, methods: {}, mounted() {} }; </script> <!-- 声明语言,并且添加scoped --> <style lang="less" scoped></style> -
vue 文件方法声明顺序
- components - props - data - computed - created - mounted - activited - update - beforeRouteUpdate - metods - watch
2. 组件命名规范
-
组件名应该始终是多个单词的,根组件 App 除外
-
有意义的名词、简短、具有可读性
-
命名遵循 PascalCase(单词首字母大写命名) 约定
-
使用遵循 kebab-case(短横线分隔命名) 约定
-
在页面中使用组件需要前后闭合,并以短线分隔,如(
<abcd-date-picker></abcd-date-picker>,<abcd-table></abcd-table>) -
导入及注册组件时,遵循 PascalCase(单词首字母大写命名) 约定
-
同时还需要注意:必须符合自定义元素规范: 切勿使用保留字。
3. props 命名规范
在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板中应该始终使用 kebab-case
<!-- 推荐 -->
<script>
props: {
greetingText: String;
}
</script>
<welcome-message greeting-text="hi"></welcome-message>
<!-- 不推荐 -->
<script>
props: {
'greeting-text': String
}
</script>
<welcome-message greetingText="hi"></welcome-message>
4. 注释规范
代码注释在一个项目的后期维护中显的尤为重要,所以我们要为每一个被复用的组件编写组件使用说明,为组件中每一个方法编写方法说明。
以下情况,务必添加注释
1.公共组件使用说明
2.各组件中重要函数或者类说明
3.复杂的业务逻辑处理说明
4.特殊情况的代码处理说明,对于代码中特殊用途的变量、存在临界值、函数中使用的hack、使用了某种算法或思路等需要进行注释描述
5.注释块必须以/**(至少两个星号)开头**/;
6.单行注释使用//;
7.多重 if 判断语句
5. 编码规范
使用 ES6 风格编码源码
-
定义变量使用
let,定义常量使用const -
静态字符串一律使用单引号或反引号,动态字符串使用反引号
// 推荐 const a = 'foobar'; const b = `foo${a}bar`; // 不推荐 const a = 'foobar'; const b = 'foo' + a + 'bar'; -
解构赋值
// 数组解构赋值 const arr = [1, 2, 3, 4]; // 推荐 const [first, second] = arr; // 不推荐 const first = arr[0]; const second = arr[1]; // 对象解构赋值 // 推荐 function getFullName(obj) { const { firstName, lastName } = obj; } // best function getFullName({ firstName, lastName }) {} // 不推荐 function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; } -
拷贝数组
使用扩展运算符(...)拷贝数组。
const items = [1, 2, 3, 4, 5]; // 推荐 const itemsCopy = [...items]; // 不推荐 const itemsCopy = items; -
箭头函数
需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了
this// 推荐 const boundMethod = (...params) => method.apply(this, params); // 不推荐 const self = this; const boundMethod = function(...params) { return method.apply(self, params); }; -
模块
-
如果模块只有一个输出值,就使用 export default,如果模块有多个输出值,就不使用 export default,export default 与普通的 export 不要同时使用
// 推荐 import myObject from './importModule'; // 不推荐 import * as myObject from './importModule'; -
如果模块默认输出一个函数,函数名的首字母应该小写
function makeStyleGuide() {} export default makeStyleGuide; -
如果模块默认输出一个对象,对象名的首字母应该大写
const StyleGuide = { es6: {} }; export default StyleGuide;
-
指令规范
-
指令有缩写一律采用缩写形式
// 推荐 :class="{'show-left':true}" @click="getListData" // 不推荐 v-bind:class="{'show-left':true}" v-on:click="getListData" -
v-for 循环必须加上 key 属性,在整个 for 循环中 key 需要唯一
<!-- 推荐 --> <ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ul> <!-- 不推荐 --> <ul> <li v-for="todo in todos"> {{ todo.text }} </li> </ul> -
避免 v-if 和 v-for 同时用在一个元素上(性能问题)
Props 规范
-
组件 props 原子化
-
提供默认值
-
使用 type 属性校验类型
-
使用 props 之前先检查该 prop 是否存在
// bad 这样做只有开发原型系统时可以接受 props: ['status']
// good props: { status: { type: String, required: true, validator: function (value) { return [ 'syncing', 'synced', 'version-conflict', 'error' ].indexOf(value) !== -1 } } }
其他
-
避免 this.$parent
-
调试信息 console.log() debugger 使用完及时删除
-
除了三目运算,if,else 等禁止简写
// bad if (true) alert(name); console.log(name); // good if (true) { alert(name); } console.log(name); -
全局变量的位置
如无特殊情况,变量
统一放到 data 内,避免组件重用时代码不被执行// 推荐 export default { data() { return { today: new Date() }; } }; // 不推荐 const today = new Date(); export default { data() { return { t: today }; } };
6. 开启 ESLint
-
目前广泛使用的有三种有google标准、airbnb标准、standard标准
-
安装
用 vue 脚手架初始化文件时,通过 vue-cli 初始化 ESLint 的配置(推荐)
-
自动修复
简单的代码风格 eslint 可以直接修复,比如我们希望 eslint 修复 src 文件夹下的 js 文件,那么在 package.json 中添加一条自定义命令
# 安装eslint模块 $ npm install eslint --save-dev # 添加eslint修复命令 "lint-fix": "eslint --fix --ext .js src/"执行
npm run lint-fix将自动修复。无法修复的将在控制台给出提示。
7. 使用Babel
我们的 javascript 代码中经常会写 ES6 代码,而这些代码在旧版本浏览器上是不被识别的,需要转换成 ES5。
-
在项目根目录创建
.babelrc文件/* 根目录 .babelrc 文件 */ { // 此项指明,转码的规则 "presets": [ // env项是借助插件babel-preset-env,下面这个配置说的是babel对es6,es7,es8进行转码,并且设置amd,commonjs这样的模块化文件,不进行转码 ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], // 下面这个是不同阶段出现的es语法,包含不同的转码插件 "stage-2" ], // 下面这个选项是引用插件来处理代码的转换,transform-runtime用来处理全局函数和优化babel编译 "plugins": ["transform-vue-jsx", "transform-runtime"], // 下面指的是在生成的文件中,保留注释 "comments": true, // 下面这段是在特定的环境中所执行的转码规则,当环境变量是下面的test就会覆盖上面的设置 "env": { "test": { "presets": ["env", "stage-2"], "plugins": ["transform-vue-jsx", "istanbul"] } } } -
使用
babel-polyfill兼容 ES61、安装
$ npm install babel-polyfill --save-dev2、找到
webpack.base.conf.js(基础配置,全局生效) 文件,修改入口如下module.exports = { entry: { app: ['babel-polyfill', './src/main.js'], }, }