Code Guide by @AlloyTeam-materliu

Standards for developing flexible, durable, and sustainable HTML and CSS, and maintainable JavaScript

通过分析github代码库总结出来的工程师代码书写习惯:GO!!!

Table of contents

Naming Project

HTML

CSS

JavaScript

Golden rule

Enforce these, or your own, agreed upon guidelines at all times. Small or large, call out what's incorrect. For additions or contributions to this Code Guide, please open an issue on GitHub.

Every line of code should appear to be written by a single person, no matter the number of contributors.

项目命名

项目名全部采用小写方式, 以中划线分隔。 比如: my-project-name

目录名

目录名参照上一条规则,有复数结构时,要采用复数命名法,比如说: scripts, styles, images, data-models

JavaScript文件命名

所有js文件名,多个单词组成时,采用中划线连接方式,比如说: 账号模型文件 account-model.js

CSS,SCSS文件命名

多个单词组成时,采用中划线连接方式,比如说:retina-sprites.scss

HTML文件命名

多个单词组成时,采用中划线连接方式,比如说: error-report.html

HTML

语法

<!DOCTYPE html>
<html>
    <head>
        <title>Page title</title>
    </head>
<body>
    <img src="images/company-logo.png" alt="Company">

    <h1 class="hello-world">Hello, world!</h1>
</body>
</html>

HTML5 doctype

在每个 HTML 页面开头使用这个简单地 doctype 来启用标准模式,使其每个浏览器中尽可能一致的展现。

虽然doctype不区分大小写,但是按照惯例,doctype大写 关于html属性,大写还是小写的一篇文章

<!DOCTYPE html>
<html>
    <head>
    </head>
</html>

Language attribute

From the HTML5 spec:

Authors are encouraged to specify a lang attribute on the root html element, giving the document's language. This aids speech synthesis tools to determine what pronunciations to use, translation tools to determine what rules to use, and so forth.

Read more about the lang attribute in the spec.

Head to Sitepoint for a list of language codes.

Sitepoint只是给出了语言代码的大类,比如说中文就只给出了ZH,但是没有区分香港,台湾,大陆等。而微软给出的一份细分了zh-cn,zh-hk,zh-tw, Head to Microsoft for a detail list of language codes.

<html lang="en-us">
    <!-- ... -->
</html>

IE compatibility mode

Internet Explorer supports the use of a document compatibility <meta> tag to specify what version of IE the page should be rendered as. Unless circumstances require otherwise, it's most useful to instruct IE to use the latest supported mode with edge mode.

For more information, read this awesome Stack Overflow article.

不同doctype在不同浏览器下的不同渲染模式,诡异模式总结的很到位.

<meta http-equiv="X-UA-Compatible" content="IE=Edge">

字符编码

通过声明一个明确的字符编码,让浏览器轻松、快速的确定适合网页内容的渲染方式。同时你也应该避免使用类似于<这种字符Entities, 而使用他们的entity_code <

<head>
    <meta charset="UTF-8">
</head>

引入 CSS 和 JavaScript

根据 HTML5 规范, 通常在引入 CSS 和 JavaScript 时不需要指明 type,因为 text/csstext/javascript 分别是他们的默认值。

HTML5 规范链接

<!-- External CSS -->
<link rel="stylesheet" href="code-guide.css">

<!-- In-document CSS -->
<style>
    /* ... */
</style>

<!-- JavaScript -->
<script src="code-guide.js"></script>

实用高于完美

尽量遵循 HTML 标准和语义,但是不应该以浪费实用性作为代价。任何时候都要用尽量小的复杂度和尽量少的标签来解决问题。

属性顺序

HTML 属性应该按照特定的顺序出现以保证易读性。

Classes 是为高可复用组件设计的,理论上他们应处在第一位。Ids 更加具体而且应该尽量少使用(例如, 页内书签),所以他们处在第二位。

<a id="..." class="..." data-modal="toggle" href="#">
    Example link
</a>

<input class="form-control" type="text">

<img src="..." alt="...">

Boolean 属性

Boolean 属性指不需要声明取值的属性。XHTML 需要每个属性声明取值,但是 HTML5 并不需要。

了解更多内容,参考 WhatWG section on boolean attributes:

一个元素中 Boolean 属性的存在表示取值 true,不存在则表示取值 false。

如果你必须为属性添加并不需要的取值,参照 WhatWG 的指引:

如果属性存在,他的取值必须是空字符串或者 [...] 属性的规范名称,不要在首尾包含空白字符。

简而言之,不要为 Boolean 属性添加取值。

<input type="text" disabled>

<input type="checkbox" value="1" checked>

<select>
    <option value="1" selected>1</option>
</select>

减少标签数量

在编写 HTML 代码时,需要尽量避免多余的父节点。很多时候,需要通过迭代和重构来使 HTML 变得更少。 参考下面的示例:

<!-- Not so great -->
<span class="avatar">
    <img src="...">
</span>

<!-- Better -->
<img class="avatar" src="...">

JavaScript 生成标签

在 JavaScript 文件中生成标签让内容变得更难查找,更难编辑,性能更差。应该尽量避免这种情况的出现。

CSS

语法

对这里提到的规则有问题吗?参考 Wikipedia 中的 CSS 语法部分

/* Bad CSS */
.selector, .selector-secondary, .selector[type=text] {
    padding: 15px;
    margin: 0px 0px 15px;
    background-color: rgba(0, 0, 0, 0.5);
    box-shadow: 0 1px 2px #CCC, inset 0 1px 0 #FFFFFF
}

/* Good CSS */
.selector,
.selector-secondary,
.selector[type="text"] {
    padding: 15px;
    margin-bottom: 15px;
    background-color: rgba(0,0,0,.5);
    box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}

声明顺序

相关的属性声明应该以下面的顺序分组处理:

  1. Positioning
  2. Box model 盒模型
  3. Typographic 排版
  4. Visual 外观

Positioning 处在第一位,因为他可以使一个元素脱离正常文本流,并且覆盖盒模型相关的样式。盒模型紧跟其后,因为他决定了一个组件的大小和位置。

其他属性只在组件 内部 起作用或者不会对前面两种情况的结果产生影响,所以他们排在后面。

关于完整的属性以及他们的顺序,请参考 Recess

.declaration-order {
    /* Positioning */
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 100;

    /* Box-model */
    display: block;
    float: right;
    width: 100px;
    height: 100px;

    /* Typography */
    font: normal 13px "Helvetica Neue", sans-serif;
    line-height: 1.5;
    color: #333;
    text-align: center;

    /* Visual */
    background-color: #f5f5f5;
    border: 1px solid #e5e5e5;
    border-radius: 3px;

    /* Misc */
    opacity: 1;
}
// 为了方便查阅, 我把Recess的order贴了一份过来, 引入时间2014-05-05

// css property order
var order = [
    'position'
    , 'top'
    , 'right'
    , 'bottom'
    , 'left'
    , 'z-index'
    , 'display'
    , 'float'
    , 'width'
    , 'height'
    , 'max-width'
    , 'max-height'
    , 'min-width'
    , 'min-height'
    , 'padding'
    , 'padding-top'
    , 'padding-right'
    , 'padding-bottom'
    , 'padding-left'
    , 'margin'
    , 'margin-top'
    , 'margin-right'
    , 'margin-bottom'
    , 'margin-left'
    , 'margin-collapse'
    , 'margin-top-collapse'
    , 'margin-right-collapse'
    , 'margin-bottom-collapse'
    , 'margin-left-collapse'
    , 'overflow'
    , 'overflow-x'
    , 'overflow-y'
    , 'clip'
    , 'clear'
    , 'font'
    , 'font-family'
    , 'font-size'
    , 'font-smoothing'
    , 'osx-font-smoothing'
    , 'font-style'
    , 'font-weight'
    , 'hyphens'
    , 'src'
    , 'line-height'
    , 'letter-spacing'
    , 'word-spacing'
    , 'color'
    , 'text-align'
    , 'text-decoration'
    , 'text-indent'
    , 'text-overflow'
    , 'text-rendering'
    , 'text-size-adjust'
    , 'text-shadow'
    , 'text-transform'
    , 'word-break'
    , 'word-wrap'
    , 'white-space'
    , 'vertical-align'
    , 'list-style'
    , 'list-style-type'
    , 'list-style-position'
    , 'list-style-image'
    , 'pointer-events'
    , 'cursor'
    , 'background'
    , 'background-attachment'
    , 'background-color'
    , 'background-image'
    , 'background-position'
    , 'background-repeat'
    , 'background-size'
    , 'border'
    , 'border-collapse'
    , 'border-top'
    , 'border-right'
    , 'border-bottom'
    , 'border-left'
    , 'border-color'
    , 'border-image'
    , 'border-top-color'
    , 'border-right-color'
    , 'border-bottom-color'
    , 'border-left-color'
    , 'border-spacing'
    , 'border-style'
    , 'border-top-style'
    , 'border-right-style'
    , 'border-bottom-style'
    , 'border-left-style'
    , 'border-width'
    , 'border-top-width'
    , 'border-right-width'
    , 'border-bottom-width'
    , 'border-left-width'
    , 'border-radius'
    , 'border-top-right-radius'
    , 'border-bottom-right-radius'
    , 'border-bottom-left-radius'
    , 'border-top-left-radius'
    , 'border-radius-topright'
    , 'border-radius-bottomright'
    , 'border-radius-bottomleft'
    , 'border-radius-topleft'
    , 'content'
    , 'quotes'
    , 'outline'
    , 'outline-offset'
    , 'opacity'
    , 'filter'
    , 'visibility'
    , 'size'
    , 'zoom'
    , 'transform'
    , 'box-align'
    , 'box-flex'
    , 'box-orient'
    , 'box-pack'
    , 'box-shadow'
    , 'box-sizing'
    , 'table-layout'
    , 'animation'
    , 'animation-delay'
    , 'animation-duration'
    , 'animation-iteration-count'
    , 'animation-name'
    , 'animation-play-state'
    , 'animation-timing-function'
    , 'animation-fill-mode'
    , 'transition'
    , 'transition-delay'
    , 'transition-duration'
    , 'transition-property'
    , 'transition-timing-function'
    , 'background-clip'
    , 'backface-visibility'
    , 'resize'
    , 'appearance'
    , 'user-select'
    , 'interpolation-mode'
    , 'direction'
    , 'marks'
    , 'page'
    , 'set-link-source'
    , 'unicode-bidi'
    , 'speak'
]

Don't use @import

Compared to <link>s, @import is slower, adds extra page requests, and can cause other unforeseen problems. Avoid them and instead opt for an alternate approach:

For more information, read this article by Steve Souders.

<!-- Use link elements -->
<link rel="stylesheet" href="core.css">

<!-- Avoid @imports -->
<style>
    @import url("more.css");
</style>

媒体查询位置

尽量将媒体查询的位置靠近他们相关的规则。不要将他们一起放到一个独立的样式文件中,或者丢在文档的最底部。这样做只会让大家以后更容易忘记他们。这里是一个典型的案例。

.element { ... }
.element-avatar { ... }
.element-selected { ... }

@media (min-width: 480px) {
    .element { ...}
    .element-avatar { ... }
    .element-selected { ... }
}

前缀属性

当使用厂商前缀属性时,通过缩进使取值垂直对齐以便多行编辑。

在 Textmate 中,使用 Text → Edit Each Line in Selection (⌃⌘A)。 在 Sublime Text 2 中, 使用 Selection → Add Previous Line (⌃⇧↑) 和 Selection → Add Next Line (⌃⇧↓)。

/* Prefixed properties */
.selector {
    -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);
            box-shadow: 0 1px 2px rgba(0,0,0,.15);
}

单条声明的声明块

在一个声明块中只包含一条声明的情况下,为了易读性和快速编辑可以考虑移除其中的换行。所有包含多条声明的声明块应该分为多行。

这样做的关键因素是错误检测 - 例如,一个 CSS 验证程序显示你在 183 行有一个语法错误,如果是一个单条声明的行,那就是他了。在多个声明的情况下,你必须为哪里出错了费下脑子。

/* Single declarations on one line */
.span1 { width: 60px; }
.span2 { width: 140px; }
.span3 { width: 220px; }

/* Multiple declarations, one per line */
.sprite {
    display: inline-block;
    width: 16px;
    height: 15px;
    background-image: url(../img/sprite.png);
}
.icon           { background-position: 0 0; }
.icon-home      { background-position: 0 -20px; }
.icon-account   { background-position: 0 -40px; }

属性简写

坚持限制属性取值简写的使用,属性简写需要你必须显式设置所有取值。常见的属性简写滥用包括:

大多数情况下,我们并不需要设置属性简写中包含的所有值。例如,HTML 头部只设置上下的 margin,所以如果需要,只设置这两个值。过度使用属性简写往往会导致更混乱的代码,其中包含不必要的重写和意想不到的副作用。

Mozilla Developer Network 有一篇对不熟悉属性简写及其行为的人来说很棒的关于 shorthand properties 的文章。

/* Bad example */
.element {
    margin: 0 0 10px;
    background: red;
    background: url("image.jpg");
    border-radius: 3px 3px 0 0;
}

/* Good example */
.element {
    margin-bottom: 10px;
    background-color: red;
    background-image: url("image.jpg");
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
}

Less 和 Sass 中的嵌套

避免不必要的嵌套。可以进行嵌套,不意味着你应该这样做。只有在需要给父元素增加样式并且同时存在多个子元素时才需要考虑嵌套。

// Without nesting
.table > thead > tr > th {  }
.table > thead > tr > td {  }

// With nesting
.table > thead > tr {
    > th {  }
    > td {  }
}

代码注释

代码是由人来编写和维护的。保证你的代码是描述性的,包含好的注释,并且容易被他人理解。好的代码注释传达上下文和目标。不要简单地重申组件或者 class 名称。

Be sure to write in complete sentences or larger comments and succinct phrases for general notes.

/* Bad example */
/* Modal header */
.modal-header {
    ...
}

/* Good example */
/* Wrapping element for .modal-title and .modal-close */
.modal-header {
    ...
}

Class 命名

/* Bad example */
.t { ... }
.red { ... }
.header { ... }

/* Good example */
.tweet { ... }
.important { ... }
.tweet-header { ... }

选择器

扩展阅读:

/* Bad example */
span { ... }
.page-container #stream .stream-item .tweet .tweet-header .username { ... }
.avatar { ... }

/* Good example */
.avatar { ... }
.tweet-header .username { ... }
.tweet .avatar { ... }

代码组织

/*
 * Component section heading
 */

.element { ... }


/*
 * Component section heading
 *
 * Sometimes you need to include optional context for the entire component. Do that up here if it's important enough.
 */

.element { ... }

/* Contextual sub-component or modifer */
.element-heading { ... }

编辑器配置

根据以下的设置来配置你的编辑器,来避免常见的代码不一致和丑陋的 diffs。

参照文档,将这些设置应用到项目的 .editorconfig 文件。 例如,Bootstrap 中的 .editorconfig 文件。 通过 关于 EditorConfig 了解更多内容。

JavaScript

Indentation,分号,单行长度

if (typeof qqfind === "undefined" ||
    typeof qqfind.cdnrejected === "undefined" ||
    qqfind.cdnrejected !== true) {
    url = "http://pub.idqqimg.com/qqfind/js/location4.js";
} else {
    url = "http://find.qq.com/js/location4.js";
}

空行

变量命名

var thisIsMyName;

var goodID;

var AndroidVersion;

var iOSVersion;

var MAX_COUNT = 10;

function Person(name) {
    this.name = name
}

字符常量

null的使用场景

不适合null的使用场景

undefined使用场景

// Bad
var person;
console.log(person === undefined);    //true

// Good
console.log(typeof person);    // "undefined"

Object Literals

// Bad
var team = new Team();
team.title = "AlloyTeam";
team.count = 25;

// Good  semi colon 采用 Followed by space 的形式
var team = {
    title: "AlloyTeam",
    count: 25
};

Array Literals

// Bad
var colors = new Array("red", "green", "blue");
var numbers = new Array(1, 2, 3, 4);


// Good
var colors = [ "red", "green", "blue" ];
var numbers = [ 1, 2, 3, 4 ];

单行注释

// Good
if (condition) {

    // if you made it here, then all security checks passed
    allowed();
}

var zhangsan = "zhangsan";    // 双斜线距离分号四个空格,双斜线后始终保留一个空格

多行注释格式

何时使用

/*
 * 注释内容与星标前保留一个空格
 */

文档注释

用在哪里

/**
 * here boy, look here , here is girl
 * @method lookGril
 * @param {Object} balabalabala
 * @return {Object} balabalabala
 */

括号对齐

// Good
if (condition) {
    doSomething();
}

if (condition)
    doSomething();
    doSomethingElse();

if else else前后留有空格

if (condition) {
    doSomething();
} else {
    doSomethingElse();
}

switch

switch (condition) {
    case "first":
        // code
        break;

    case "third":
        // code
        break;

    default:
    // code
}


switch (condition) {

    // obvious fall through    // 这里为啥JSHint默认就会放过,因为 case "first" 内无内容
    case "first":
    case "second":
        // code
        break;

    case "third":
    // code

    /* falls through */ // 这里为啥要加这样的注释, 明确告知JSHint放过此处告警
    default:
    // code
}

switch(condition) {
    case "first":
        // code
        break;

    case "second":
        // code
        break;

    // no default
}

for

var values = [ 1, 2, 3, 4, 5, 6, 7 ],
    i, len;

for (i=0, len=values.length; i<len; i++) {
    process(values[i]);
}



var prop;

for (prop in object) {

    // 注意这里一定要有 hasOwnProperty 的判断, 否则 JSLint 或者 JSHint 都会有一个 warn !
    if (object.hasOwnProperty(prop)) {
        console.log("Property name is " + prop);
        console.log("Property value is " + object[prop]);
    }
}

变量声明

function doSomethingWithItems(items) {

    var value = 10,    // 注释啊,注释啊,亲
        result = value + 10,    // 注释啊,注释啊
        i,    // 注释啊,注释啊,亲
        len;    // 注释啊,注释啊,亲

    for (i=0, len=items.length; i < len; i++) {
        doSomething(items[i]);
    }
}

函数声明

function doSomething(item) {
    // do something
}

var doSomething = function (item) {
    // do something
}


// Good
doSomething(item);

// Bad: Looks like a block statement
doSomething (item);


// Good
var value = (function() {

    // function body
    return {
        message: "Hi"
    }
}());


// Good
(function() {
    "use strict";

    function doSomething() {
        // code
    }

    function doSomethingElse() {
        // code
    }

})();

杂项