美文网首页前端大牛
前端代码规范

前端代码规范

作者: lip2up | 来源:发表于2018-04-02 14:29 被阅读66次

    该文于 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 编码

    路径和文件名

    • 不要在路径和文件名中使用空格
    • view/css/js/img 文件,统一采用陀峰风格,请不要用 - 或 _ 连接各个单词,更不要不区分单词,一律小写

    二、HTML 书写规范

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

    基本页面

    采用如下模板:

    <!doctype html>
    <html lang='zh-CN'>
    <head>
    <meta charset='utf-8'>
    <title>page title</title>
    </head>
    <body>
    </body>
    </html>
    
    • doctype 采用 html5 建议的 doctype,废弃 xhtml 的 doctype

    • charset 采用 html5 建议的 charset,不要使用:

      <meta http-equiv="charset" content="utf-8">
      
    • charset 使用 utf-8

    标签闭合

    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 使用陀峰命名法,不要使用 -_

    例如:

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

    三、CSS 书写规范

    类名和 ID

    • 永远不要使用 ID 进行样式化,只使用类名,例如:

      // 不当的样式
      #userBox {
          ...
      }
      
      // 良好的样式,使用类名
      .user-box {
          ...
      }
      

    类名用于样式化,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 的样式

    • 永远不要针对 div/span/p 的样式,这些标签太通用:

      // 不当的
      .user-box div
      .news-box span
      .article-list p
      
    • 可以对 em strong 等含有丰富语义的标签应用样式,但不要嵌套

      // 恰当的
      .user-list em {
          ...
      }
      // 不当的,进行了多级嵌套
      .user-list li em {
          ...
      }
      
      // 最不当的,单独的 tag 样式是魔鬼
      // 合理的 css reset 除外,全站应有且只有一个统一的 css reset
      em {
          ...
      }
      
    • 无论如何,少用 tag 样式

    注释

    • less 中允许使用 // 注释,而 css 语法中不允许,只能使用 /**/

    四、JS 书写规范

    ; 号的使用

    • 虽然很多正规的教程,都要求每条 js 语句都应以 ; 结束,但这里,我们选择所有的语句省略

    • ; 浪费了键盘输入,窃以为是 js 中的糟粕,不用纠结,大胆省略吧

    • 有时,省略 ; 会导致代码出错,可加一个前置 ;,例如

      var str = ''
      ; [1, 2, 3].forEach(function(n) {
          console.log(n)
      })
      

    代码风格

    // 所有的二元运算符前后应有空格
    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/

    相关文章

      网友评论

        本文标题:前端代码规范

        本文链接:https://www.haomeiwen.com/subject/rabthftx.html