前端大牛

前端代码规范

2018-04-02  本文已影响66人  lip2up

该文于 2013 年创作,经年修改,录以记之

统一的规范有助于团队合作开发,但规范又臭又长,又不利于阅读与遵守,所以本规范尽量压缩。另外,本规范只是草案,还需要在实践中不断完善。

一、全局规范

tab or space

考虑到各代码编辑器的差异,前端统一采用 4 个空格 作为缩进标准。
设置 4 个空格后,向前删除空格时就会很痛苦,可在编辑器中,设置按下退格键时,一次删掉前面的四个空格。

Sublime Text 是这样设置的:

"tab_size": 4,
"translate_tabs_to_spaces": true,

VIM 是这样设置的:

set autoindent softtabstop=4 shiftwidth=4 expandtab

其他编辑器请自行摸索。

UNIX \n or Windows \r\n

统一使用 UNIX 的换行符

文件编码

统一使用不带 BOM 字节的 UTF-8 编码

路径和文件名

二、HTML 书写规范

秉承 HTML5 规范与精神,结合 XHTML,制定本规范。

基本页面

采用如下模板:

<!doctype html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<title>page title</title>
</head>
<body>
</body>
</html>

标签闭合

xhtml 规定,标签必须闭合,对没有内容的标签,也要写一个 /,例如:

<input type='text' name='username'/>
<br/>

html5 拨乱反正,回归 html 本质,所以,我们这样写:

<input type='text' name='username'>
<br>

疯狂的 html5 甚至允许这样:

<table>
    <tr>
        <th>header1
        <th>header2
    <tr>
        <td>cell11
        <td>cell12
    <tr>
        <td>cell21
        <td>cell22
<p>content out of table, no table close tag before
<p>second paragraph

我们不要这么疯狂,这样会弄乱我们的代码,所以,对于没有内容的标签,我们不必用 / 关闭,对于有内容的标签,我们仍然遵从 xhtml 的要求

属性和属性值

html5 允许对属性值不加任何引号,但我们不要这么做,我们还是要加引号的,引号可以用单引号,也可以用双引号,推荐使用单引号,因为输入双引号时,要同时按 Shift 键,例如:

<meta charset='utf-8'>
<input type='text' name='username'>
 
<div class='page cl'>

没有值的属性

罗嗦的 xhtml 规定,对没有值的属性这样处理:

<input type='radio' checked='checked'/>

但接受 html5 鼓励的我们,应该这样写:

<input type='radio' checked>

常见标签简洁写法

省略 script/link 标签的 type 属性,这不是必需的,例如:

罗嗦的写法:
<script src='...' type='text/javascript'></script>
<link href='...' type='text/css' rel='stylesheet'>
 
简洁的写法:
<script src='...'></script>
<link href='...' rel='stylesheet'>

标签顺序

head 标签内的标签顺序,推荐:

<meta>
<title>
<link>

script 标签的位置

<script> 应放置在 </body> 前,而不是 <head> 内,放在 <head> 内的脚本会阻塞页面,导致页面性能下降

类名和 ID

例如:

不当的类名和 ID
<div class='userBox' id='user_box'>
<div class='user_box' id='user-box'>

恰当的类名和 ID
<div class='user-box' id='userBox'>

三、CSS 书写规范

类名和 ID

类名用于样式化,ID 用于脚本访问

格式风格

不当的格式风格和推荐的格式风格对比:

// 不当的,{ 应放在类名后,而不是重启一行
.user-box
{
    ...
}
// 应该这样:
.user-box {
    ...
}

// 不当的样式
.user-box{
    ...
}
// 应该这样,{ 与类名之间应该有空格
.user-box {
    ...
}
 
// 不当的,样式不应写在一行:
.user-box { font-size: 14px; line-height: 2; }
// 应该这样:
.user-box {
    font-size: 14px;
    line-height: 2;
}
 
// 不当的样式
font-size:14px;
// 应该这样,键和值应以空格隔开
font-size: 14px;
 
// 不当的,最后一行样式也应以 ; 结束
.user-box {
    font-size: 14px;
    line-height: 2
}
// 应该这样:
.user-box {
    font-size: 14px;
    line-height: 2;
}
 
// 不当的样式
.user-list a, .news-list a {
    ...
}
// 应该这样,每个 selector 占一行
.user-list a,
.news-list a {
    ...
}

层级不可过多

// 不当,层级过多
.user-box .user-list .user-item .user-avatar {
    ...
}
// 应该减少层级,这样能提高 CSS 性能
.user-box .user-avatar {
    ...
}
// 采用命名空间的情况下,最好一级
.user-avatar {
    ...
}
// 这种情况下,即 .class tag,可以两级
.user-avatar a {
    ...
}
// 最长不要超过三级

使用命名空间

多使用 namespace-* 形式的类名,每个业务场景定义一个前缀作为命名空间,如用 site- 作为全站的命名空间,用 user- 作为用户中心的命名空间,用 news- 作为咨询的命名空间:

.site-top
.user-nav
.news-left

针对 tag 的样式

注释

四、JS 书写规范

; 号的使用

代码风格

// 所有的二元运算符前后应有空格
var str = 'hi, ' + name
 
// 函数定义与 { 应处于同一行,并以空格分割
function lineTo(from, to) {
    ...
}
 
// 应尽量避免写 while 与 do .. while 循环,用 for 替代
// 可以尝试用 ECMAScript 5 新规范新增的 Array 方法,彻底替代循环结构,这些方法有:
// Array.prototype.forEach
// Array.prototype.map
// Array.prototype.every
// Array.prototype.some
// Array.prototype.filter
// Array.prototype.reduce
// Array.prototype.reduceRight
// 这些方法的使用,符合函数式理念,一旦习惯,就会爱上这种方式
 
// for 与 (,for 内的 ; 之间,) 与 { 都应有空格
for (var i = 1; i <= 9; i++) {
    ...
}
 
// if 与 for 同理,else 应与结束的 } 处于同一行
if (sex == 'girl') {
    ...
} else if (sex == 'boy') {
    ...
} else {
    ...
}
 
// 始终在 if/else/for 中使用 {},那怕只有一条语句
if (condition) {
    // only one statement
    return true
}
 
// switch 的 case 应该这样缩进,default 应始终在最后
switch (sex) {
    case 'boy':
        ....
        break;
    case 'girl':
        ....
        break;
    default:
        ...
}
 
// 尽量避免使用 switch 语句,可以采用这样的结构
var map = {
    boy: ...,
    girl: ...
}
var val = map[sex]
 
// 甚至可以用 map 执行函数
var actionMap = {
    doThis: function() {
        ...
    },
    doThat: function() {
        ...
    }
}
 
// 函数调用与 ( 之间不应有空格,以便和 for if 语句加以区分
lineTo(5, 6)
 
// 永远不要这样写
var arr = new Array()
var obj = new Object()
// 应这样写
var arr = []
var obj = {}
 
// 一行一个变量,应该这样
var v1 = 1
var v2 = 2
// 避免这样
var v1 = 1, v2 = 2
 
// 变量名和函数名采用陀峰样式,不要使用 _,如
var myVar = 22

// 尽量使用 ES6 的语法,前提是环境支持(或 webpack + babel 编译支持,或原生环境支持)

尽量保持一个出口

很多人这样写代码:

function someFunc(...) {
    if (aCondition) {
        ...
        return aValue
    }
    if (bCondition) {
        ...
        return bValue
    }
    ...
    return cValue
}

这样的代码,说实话,在我眼里,很拙劣,无论你有千般理由!什么性能啦、逻辑清晰啦,都是你对代码没有掌控力的借口!

不要纠结于局部的、指令级的性能,你不是在写汇编!

多处 return,你是爽了,但这种代码,很容易出现维护性问题。

我宁愿多用嵌套 if ... else,始终保持一个出口,函数采用三段式(第一段前置全函数变量声明、第二段逻辑处理、第三段返回)来写,例如:

function someFunc(...) {
    var result
 
    if (...) {
        if (...) {
            ...
        } else {
            ...
        }
    } else {
        ...
    }
 
    return result
}

如果嵌套过多,可以将之提取出来,放到一个新函数里,并且,给这个新函数,取一个含义明确的名字,这样代码就能实现“自注释”,例如:

function someFunc(...) {
    var result
 
    if (...) {
        result = newFuncWithWellDescription(...) 
    } else {
        ...
    }
 
    return result
}
 
function newFuncWithWellDescription(...) {
    ...
}

并且,应在 else 里处理例外情况(错误处理),在 if 里处理正常流程。

不要怕函数多了影响性能,记住,你不是在写汇编,局部的、指令级的性能优化是没有意义的,系统整体的性能优化,才是重点

变量就地声明,声明的同时进行赋值

有些人这样写:

function someFunc(items) {
    var i, len = items.length
    ...
 
    for (i = 0; i < len; i++) {
        ...
    }
 
    ...
}

这样写没有任何好处,i 只是一个循环变量,没有必要像 C 语言那样作前置声明,应该直接这样:

function someFunc(items) {
    ...
 
    for (var i = 0, len = items.length; i < len; i++) {
        ...
    }
 
    ...
}

变量就地声明并赋值,赋值后保持不变,不仅仅符合函数式编程思想,也是一种良好的编程习惯

嵌套函数

嵌套函数应该这样写:

function doSomething(...) {
    ...
    var doSomethingInner = function(...) {
        ...
    }
    ...
}

而不应该这样:

function doSomething(...) {
    ...
    function doSomethingInner(...) {
        ...
    }
    ...
}

要牢记,在 JS 这类函数式语言中,函数也是值

在 seajs 中,所有的函数都声明在 define(function() { ... }) 内,也即,所有的函数都是嵌套函数,但这里,我们忽略 seajs 这一级,将 seajs 内的第一级函数,当成顶级函数,例如:

define(function(require) {
    ...
    function someFunc(...) {
        ...
    }
    ...
})

而不要这样:

define(function(require) {
    ...
    var someFunc = function(...) {
        ...
    }
    ...
})

佛说,不要着相

五、函数式编程

js 本质上是函数式编程语言,受当时面向对象思潮的影响,也沾染上一些面向对象的糟粕。让我们去芜存菁,回归函数式本质。

关于函数式,可通过搜索引擎进行学习,这里只谈函数式的一个很重要的特性:不变性。

不变性

简单理解,一个变量,一旦定义并赋值(定义时同时赋值,是一个良好的编程习惯),其值应保持不变。

看以下代码:

function onSuccess(data) {
    data = parseJson(data)
    if (data.s == 200) {
        ...
    }
}

上述代码存在的问题是,data 的值在第一行发生了变化!即使在命令式编程语言中,这种代码也是很拙劣的!

一般,稍微有经验的程序员,无论是否受到过函数式编程思想的影响,都不会写出这样的代码。多声明一个变量会死呀?应该这样:

function onSuccess(data) {
    var json = parseJson(data)
    if (json.s == 200) {
        ...
    }
}

再看以下代码:

function doubleIt(items) {
    for (var i = 0, len = items.length; i < len; i++) {
        items[i] = items[i] * 2
    }
    return items
}

很多命令式编程语言的程序员,觉得上述代码没什么,甚至会告诉你,这样节省内存

上述代码不符合函数式精神,items 中的元素,仍然发生了变化,这样写:

function doubleIt(items) {
    var newItems = []
 
    for (var i = 0, len = items.length; i < len; i++) {
        newItems[i] = items[i] * 2
    }
 
    return newItems
}

上述代码,只有循环变量 i 在变,并且局限在局部。更彻底地,我们使用 Array.prototype.map:

function doubleIt(items) {
    return items.map(function(item) {
        return item * 2
    })
}

回忆高中时,数学中关于函数的定义:函数是一个集合到另一个集合!这样的代码,才算是,真正回归了,编程语言的数学本质

不要担心函数调用的性能,现代的 JS 引擎,会对这样的调用进行优化

JS 性能点不在这里,而在 DOM 操作。一个 DOM 操作所耗时间,约是 JS 对象操作的 1000 倍以上,所以应该尽量减少 DOM 操作

六、补充材料

解读ECMAScript[1]——执行环境、作用域及闭包
解读ECMAScript[2]——函数、构造器及原型
闭包漫谈(从抽象代数及函数式编程角度)
CSS 相对绝对定位系列(一)
SeaJS 官方文档
LESS 中文文档
使用SeaJS实现模块化JavaScript开发

七、拥抱 ES6+

从现在开始,重视并书写 ES6+,这里有个很好的教程 http://es6.ruanyifeng.com/

上一篇下一篇

猜你喜欢

热点阅读