HuYuee/blog

前端开发规范

HuYuee opened this issue · 0 comments

规范有很多,但是可以参考下面规范👇

原则和目的

基于开发共识,结合实际项目,坚持制定好的代码规范。确保同一团队以及不同的项目间,都能够实现代码风格的一致性,提高可维护性、可读性。

细则

命名规则和项目目录规范

命名规则

项目命名

全部采用小写方式, 以下划线分隔。

例:my_project_name

目录命名

参照项目命名规则;

有复数结构时,要采用复数命名法。

例:scripts, styles, images, data_models

JS文件命名

参照项目命名规则。

例:account_model.js

CSS, SCSS文件命名

参照项目命名规则。

例:retina_sprites.scss

HTML文件命名

参照项目命名规则。

例:error_report.html

项目目录规范

│  .babelrc
│  .gitignore
│  LICENSE
│  package.json
│  postcss.config.js
│  README.md
│  uba.config.js
│  uba.mock.js
│
├─dist
│  ├─assets
│  │   ├─css
│  │   ├─js
│  │   ├─images
│  │     
│  │ index.html
├─mock
│  └─index.json
└─src
    │  constant.js
    │  index.html
    │  index.js
    │  index.less
    │
    ├─assets
    │  README.md
    │
    ├─components
    │  │  index.js
    │  ├─LoadingTable
    │     index.css
    │     index.jsx
    │
    ├─pages
    │  │  index.js
    │  │
    │  ├─App
    │     index.css
    │     index.jsx
    │  
    └─api
            index.js

HTML 编码规范

语法

  • 缩进使用soft tab(4个空格);
  • 嵌套的节点应该缩进;
  • 在属性上,使用双引号,不要使用单引号;
  • 属性名全小写,用中划线做分隔符;
  • 不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的);
  • 不要忽略可选的关闭标签,例:</li></body>
<!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

  • 在页面开头使用这个简单地doctype来启用标准模式,使其在每个浏览器中尽可能一致的展现;
  • 虽然doctype不区分大小写,但是按照惯例,doctype大写 (关于html属性,大写还是小写)。
<!DOCTYPE html>
<html>
	...
</html>

lang属性

  • 根据HTML5规范:

应在html标签上加上lang属性。这会给语音工具和翻译工具帮助,告诉它们应当怎么去发音和翻译。

  • 更多关于 lang 属性的说明在这里
  • 在sitepoint上可以查到语言列表

sitepoint 只是给出了语言的大类,例如中文只给出了zh,但是没有区分香港,**,大陆。而微软给出了一份更加详细的语言列表,其中细分了 zh-cn, zh-hk, zh-tw

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

字符编码

通过声明一个明确的字符编码,让浏览器轻松、快速的确定适合网页内容的渲染方式,通常指定为'UTF-8'。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    ...
</html>

IE兼容模式

<meta> 标签可以指定页面应该用什么版本的IE来渲染;

如果你想要了解更多,请点击这里

不同 doctype 在不同浏览器下会触发不同的渲染模式(这篇文章总结的很到位)。

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=Edge">
    </head>
    ...
</html>

引入CSS, JS

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

HTML5 规范链接:

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

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

<!-- External JS -->
<script src="code_guide.js"></script>

<!-- In-document JS -->
<script>
    ...
</script>

属性顺序

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

  • class
  • id
  • name
  • data-*
  • src, for, type, href, value , max-length, max, min, pattern
  • placeholder, title, alt
  • aria-*, role
  • required, readonly, disabled

class是为高可复用组件设计的,所以应处在第一位;

id更加具体且应该尽量少使用,所以将它放在第二位。

<a class="..." id="..." 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。

<input type="text" disabled>

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

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

JS生成标签

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

减少标签数量

在编写HTML代码时,需要尽量避免多余的父节点;

很多时候,需要通过迭代和重构来使HTML变得更少。

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

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

实用高于完美

尽量遵循HTML标准和语义,但是不应该以浪费实用性作为代价;

任何时候都要用尽量小的复杂度和尽量少的标签来解决问题。

CSS 编码规范

以下为前端开发团队遵循和约定的 CSS 编码规范。

代码风格

  • 代码应该符合 CSS 语法有效性,可以使用 W3C CSS validator 工具来验证。

  • ID 和 Class 应该按照元素功能命名,不应该按照元素表现命名,命名应该含义清晰。

    /* bad: 含义不清 */
    #yee-1901 {}
    
    /* bad: 表现化 */
    .button-green {}
    .clear {}
    
    /* good: 功能化 */
    #gallery {}
    #login {}
    .video {}
  • ID 和 Class 命名应该在保持含义清晰的前提下尽可能简短。

    /* bad */
    #navigation {}
    .atr {}
    
    /* good */
    #nav {}
    .author {}
  • ID 和 Class 命名中单词应该全部小写,单词之间使用 - 作为分隔符。

    /* bad */
    #videoId {}
    .demoimage {}
    .error_status {}
    
    /* good */
    #video-id {}
    .ads-sample {}
  • 不能「MUST NOT」把 ID 和 Class 选择符作为类型选择符的限定符,这样做没必要,反而还影响性能。

    /* bad */
    ul#example {}
    div.error {}
    
    /* good */
    #example {}
    .error {}
  • CSS 属性应该尽可能使用简化方式书写,需注意简写时默认值的副作用,详细参考 Shorthand properties

    /* bad */
    border-top-style: none;
    font-family: palatino, georgia, serif;
    font-size: 100%;
    line-height: 1.6;
    padding-bottom: 2em;
    padding-left: 1em;
    padding-right: 1em;
    padding-top: 0;
    
    /* good */
    border-top: 0;
    font: 100%/1.6 palatino, georgia, serif;
    padding: 0 1em 2em;
  • CSS 属性中的 0 值不应该带单位。

    /* bad */
    margin: 0px;
    padding: 0px;
    
    /* good */
    margin: 0;
    padding: 0;
  • CSS 属性中数值介于-1到1之间的小数应该忽略开头的 0

    /* bad */
    font-size: 0.8em;
    
    /* good */
    font-size: .8em;
  • CSS 的色值应该尽可能使用简化写法。

    /* bad */
    color: #eebbcc;
    
    /* good */
    color: #ebc;

组织格式

  • 必须采用 4 个空格为一次缩进。

  • CSS 属性声明应该按字母升序排列。

    /* good */
    background: fuchsia;
    border: 1px solid;
    -moz-border-radius: 4px;
    -webkit-border-radius: 4px;
    border-radius: 4px;
    color: black;
    text-align: center;
    text-indent: 2em;
  • CSS 每个代码块相对于父代码库必须有缩进。

    /*good*/
    @media screen, projection {
    
      html {
        background: #fff;
        color: #444;
      }
    
    }
  • CSS 属性声明必须以分号结尾。

  • CSS 属性名冒号后必须有一个空格。

    /* bad */
    color:#eebbcc;
    
    /* good */
    color: #ebc;
  • CSS 中的属性名建议按照字母顺序排列,可以使用 Sublime Text 的 F5 命令来自动格式化。

  • 最后的选择符与 { 之间必须有一个空格。

    /* bad */
    #video{
      margin-top: 1em;
    }
    .author
    {
      margin-top: 1em;
    }
    
    /* good */
    #video {
      margin-top: 1em;
    }
  • 多个并列的选择符必须换行。

    /* bad */
    a:focus, a:active {
      position: relative; top: 1px;
    }
    
    /* good */
    h1,
    h2,
    h3 {
      font-weight: normal;
      line-height: 1.2;
    }
  • CSS 规则之间必须以空白行分隔。

    /* good */
    html {
      background: #fff;
    }
    
    body {
      margin: auto;
      width: 50%;
    }
  • CSS 属性值中所有使用到引号的位置必须使用单引号。

    /* bad */
    @import url("//www.google.com/css/maia.css");
    
    html {
      font-family: "open sans", arial, sans-serif;
    }
    
    /* good */
    @import url('//www.google.com/css/maia.css');
    
    html {
      font-family: 'open sans', arial, sans-serif;
    }

代码注释

  • CSS规则段落之前应该添加注释说明。

    /* good */
    /* Header */
    
    #adw-header {}
    
    /* Footer */
    
    #adw-footer {}
    
    /* Gallery */
    
    .adw-gallery {}

JavaScript 编码规范

以下内容前端开发团队遵循和约定的 JavaScript 编码规范。

缩进

使用soft tab(4个空格)。

    var x = 1,
        y = 1;

    if (x < y) {
        x += 10;
    } else {
        x += 1;
    }

单行长度

不要超过 80,但如果编辑器开启 word wrap可以不考虑单行长度。

类型

  • 原始值: 相当于传值

    • string
    • number
    • boolean
    • null
    • undefined
    var foo = 1,
        bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9
  • 复杂类型: 相当于传引用

    • object
    • array
    • function
    var foo = [1, 2],
        bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9

对象

  • 使用字面值创建对象

    // bad
    var item = new Object();
    
    // good
    var item = {};
  • 不要使用保留字 reserved words 作为键

    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };

数组

  • 使用字面值创建数组

    // bad
    var items = new Array();
    
    // good
    var items = [];
  • 如果你不知道数组的长度,使用push

    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
  • 当你需要拷贝数组时使用slice

    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
  • 使用slice将类数组的对象转成数组.

    function trigger() {
      var args = Array.prototype.slice.call(arguments);
      ...
    }

字符串

  • 对字符串使用单引号 ''

    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
  • 超过80个字符的字符串应该使用字符串连接换行

    • 注: 如果过度使用,长字符串连接可能会对性能有影响.
    // bad
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // bad
    var errorMessage = 'This is a super long error that \
    was thrown because of Batman. \
    When you stop to think about \
    how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // good
    var errorMessage = 'This is a super long error that ' +
    'was thrown because of Batman.' +
    'When you stop to think about ' +
    'how Batman had anything to do ' +
    'with this, you would get nowhere ' +
    'fast.';
  • 编程时使用join而不是字符串连接来构建字符串,特别是IE

    var items,
        messages,
        length, i;
    
    messages = [{
      state: 'success',
      message: 'This one worked.'
    },{
      state: 'success',
      message: 'This one worked as well.'
    },{
      state: 'error',
      message: 'This one did not work.'
    }];
    
    length = messages.length;
    
    // bad
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // good
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = messages[i].message;
      }
    
      return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
    }

函数

  • 函数表达式:

    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 有名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用函数表达式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
  • 绝对不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但是它们解析不同。

    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
    • 绝对不要把参数命名为 arguments, 这将会逾越函数作用域内传过来的 arguments 对象.
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }

属性

  • 当使用变量访问属性时使用中括号.

    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');

变量

  • 总是使用 var 来声明变量,如果不这么做将导致产生全局变量,我们要避免污染全局命名空间。

    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
  • 使用一个 var 以及新行声明多个变量,缩进4个空格。

    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    • 最后再声明未赋值的变量,当你想引用之前已赋值变量的时候很有用。
    // bad
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // bad
    var i, items = getItems(),
        dragonball,
        goSportsTeam = true,
        len;
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball,
        length,
        i;
    • 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。
    // bad
    function() {
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // good
    function() {
      var name = getName();
    
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // bad
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    
    // good
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }

条件表达式和等号

  • 适当使用 ===!== 以及 ==!=.

  • 条件表达式的强制类型转换遵循以下规则:

    • 对象 被计算为 true
    • Undefined 被计算为 false
    • Null 被计算为 false
    • 布尔值 被计算为 布尔的值
    • 数字 如果是 +0, -0, or NaN 被计算为 false , 否则为 true
    • 字符串 如果是空字符串 '' 则被计算为 false, 否则为 true
    if ([0]) {
      // true
      // An array is an object, objects evaluate to true
    }
  • 使用快捷方式.

    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }

  • 给所有多行的块使用大括号

    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }

注释

  • 使用 /** ... */ 进行多行注释,包括描述,指定类型以及参数值和返回值

    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
    * make() returns a new element
    * based on the passed in tag name
    * @param <String> tag
    * @return <Element> element
    */
    function make(tag) {
        // ...stuff...
        return element;
    }
  • 使用 // 进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行.

    // bad
    var active = true;  // is current tab
    
    // good
    // is current tab
    var active = true;
    
    // bad
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
    // good
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    • 如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 FIXMETODO 帮助其他人迅速理解
    function Calculator() {
    
      // FIXME: shouldn't use a global here
      total = 0;
    
      return this;
    }
    function Calculator() {
    
      // TODO: total should be configurable by an options param
      this.total = 0;
    
      return this;
    }

空白

  • 将tab设为4个空格

    // bad
    function() {
      ∙∙var name;
    }
    
    // bad
    function() {
      ∙var name;
    }
    
    // good
    function() {
      ∙∙∙∙var name;
    }
  • 大括号前放一个空格

    // bad
    function test(){
      console.log('test');
    }
    
    // good
    function test() {
      console.log('test');
    }
    
    // bad
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
    // good
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    • 在做长方法链时使用缩进.
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // good
    $('#items')
      .find('.selected')
      .highlight()
      .end()
      .find('.open')
      .updateCount();
    
    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
        .attr('width',  (radius + margin) * 2).append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    
    // good
    var leds = stage.selectAll('.led')
        .data(data)
      	.enter().append('svg:svg')
        .class('led', true)
        .attr('width',  (radius + margin) * 2)
      	.append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);

逗号

  • 不要将逗号放前面

    // bad
    var once
      , upon
      , aTime;
    
    // good
    var once,
        upon,
        aTime;
    
    // bad
    var hero = {
        firstName: 'Bob'
      , lastName: 'Parr'
      , heroName: 'Mr. Incredible'
      , superPower: 'strength'
    };
    
    // good
    var hero = {
      firstName: 'Bob',
      lastName: 'Parr',
      heroName: 'Mr. Incredible',
      superPower: 'strength'
    };
  • 不要加多余的逗号,这可能会在IE下引起错误,同时如果多一个逗号某些ES3的实现会计算多数组的长度。

    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];

分号

  • 语句结束一定要加分号

    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();

类型转换

  • 在语句的开始执行类型转换.

  • 字符串:

    //  => this.reviewScore = 9;
    
    // bad
    var totalScore = this.reviewScore + '';
    
    // good
    var totalScore = '' + this.reviewScore;
    
    // bad
    var totalScore = '' + this.reviewScore + ' total score';
    
    // good
    var totalScore = this.reviewScore + ' total score';
  • 对数字使用 parseInt 并且总是带上类型转换的基数.

    var inputValue = '4';
    
    // bad
    var val = new Number(inputValue);
    
    // bad
    var val = +inputValue;
    
    // bad
    var val = inputValue >> 0;
    
    // bad
    var val = parseInt(inputValue);
    
    // good
    var val = Number(inputValue);
    
    // good
    var val = parseInt(inputValue, 10);
    
    // good
    /**
    * parseInt was the reason my code was slow.
    * Bitshifting the String to coerce it to a
    * Number made it a lot faster.
    */
    var val = inputValue >> 0;
  • 布尔值:

var age = 0;

// bad
var hasAge = new Boolean(age);

// good
var hasAge = Boolean(age);

// good
var hasAge = !!age;

命名约定

  • 避免单个字符名,让你的变量名有描述意义。
// bad
function q() {
  // ...stuff...
}

// good
function query() {
  // ..stuff..
}
  • 当命名对象、函数和实例时使用驼峰命名规则
// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
var this-is-my-object = {};
function c() {};
var u = new user({
  name: 'Bob Parr'
});

// good
var thisIsMyObject = {};
function thisIsMyFunction() {};
var user = new User({
  name: 'Bob Parr'
});
  • 当命名构造函数或类时使用驼峰式大写

    ```javascript
    // bad
    function user(options) {
      this.name = options.name;
    }
    
    var bad = new user({
      name: 'nope'
    });
    
    // good
    function User(options) {
      this.name = options.name;
    }
    
    var good = new User({
      name: 'yup'
    });
    ```
    
    • 命名私有属性时前面加个下划线 _

      // bad
      this.__firstName__ = 'Panda';
      this.firstName_ = 'Panda';
      
      // good
      this._firstName = 'Panda';
  • 当保存对 this 的引用时使用 _this.

    ```javascript
    // bad
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    
    // bad
    function() {
      var that = this;
      return function() {
        console.log(that);
      };
    }
    
    // good
    function() {
      var _this = this;
      return function() {
        console.log(_this);
      };
    }
    ```
    

存取器

  • 属性的存取器函数不是必需的
  • 如果你确实有存取器函数的话使用getVal() 和 setVal('hello')
// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);
  • 如果属性是布尔值,使用isVal() 或 hasVal()
// bad
if (!dragon.age()) {
  return false;
}

// good
if (!dragon.hasAge()) {
  return false;
}
  • 可以创建get()和set()函数,但是要保持一致
function Jedi(options) {
  options || (options = {});
  var lightsaber = options.lightsaber || 'blue';
  this.set('lightsaber', lightsaber);
}

Jedi.prototype.set = function(key, val) {
  this[key] = val;
};

Jedi.prototype.get = function(key) {
  return this[key];
};

构造器

  • 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。

    function Jedi() {
      console.log('new jedi');
    }
    
    // bad
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // good
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
  • 方法可以返回 this 帮助方法可链。

    // bad
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20) // => undefined
    
    // good
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
      return this;
    };
    
    var luke = new Jedi();
    
    luke.jump()
      .setHeight(20);
  • 可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。

    function Jedi(options) {
      options || (options = {});
      this.name = options.name || 'no name';
    }
    
    Jedi.prototype.getName = function getName() {
      return this.name;
    };
    
    Jedi.prototype.toString = function toString() {
      return 'Jedi - ' + this.getName();
    };

事件

  • 当给事件附加数据时,传入一个哈希而不是原始值,这可以让后面的贡献者加入更多数据到事件数据里而不用找出并更新那个事件的事件处理器

    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });

    更好:

    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
      // do something with data.listingId
    });

模块

  • 模块应该以 ! 开始,这保证了如果一个有问题的模块忘记包含最后的分号在合并后不会出现错误
  • 这个文件应该以驼峰命名,并在同名文件夹下,同时导出的时候名字一致
  • 加入一个名为noConflict()的方法来设置导出的模块为之前的版本并返回它
  • 总是在模块顶部声明 'use strict';
// fancyInput/fancyInput.js

!function(global) {
  'use strict';

  var previousFancyInput = global.FancyInput;

  function FancyInput(options) {
    this.options = options || {};
  }

  FancyInput.noConflict = function noConflict() {
    global.FancyInput = previousFancyInput;
    return FancyInput;
  };

  global.FancyInput = FancyInput;
}(this);

jQuery

  • 缓存jQuery查询
// bad
function setSidebar() {
  $('.sidebar').hide();

  // ...stuff...

  $('.sidebar').css({
    'background-color': 'pink'
  });
}

// good
function setSidebar() {
  var $sidebar = $('.sidebar');
  $sidebar.hide();

  // ...stuff...

  $sidebar.css({
    'background-color': 'pink'
  });
}
  • 对DOM查询使用级联的 $('.sidebar ul')$('.sidebar ul')
  • 对有作用域的jQuery对象查询使用 find
// bad
$('.sidebar', 'ul').hide();

// bad
$('.sidebar').find('ul').hide();

// good
$('.sidebar ul').hide();

// good
$('.sidebar > ul').hide();

// good (slower)
$sidebar.find('ul');

// good (faster)
$($sidebar[0]).find('ul');

ECMAScript 5兼容性

性能

react组件开发规范

要求编码规范,接口定义规范,组件结构自由给予用户充分定制能力。

文件命名

  • 每一个文件只包含一个组件,每一个基本组件只包含单一功能
  • src目录下,如果文件返回是一个类,文件名首字母大写
  • 文件js模块统一使用js后缀名
  • 测试用例文件名使用.spec.js后缀
  • 每一个组件使用一个单独的测试用例文件

js规范

  • 使用es6开发,尽量使用常用的ES6语法,(ES6语法参考)[http://es6.ruanyifeng.com/]

  • 使用jsx语法

  • 组件仓库命名为小写和“-”连接,如button、button-group

  • 组件文件命名使用大驼峰, ComponentDemo

  • 带命名空间的组件,如果一个组件包含只有自身使用的子组件,以该组件为命名空间编写组件,例如Table,Table.Head

  • 不使用displayName命名

  • 自定义属性使用data-

  • 使用propTypes进行props类型校验

  • 使用defaultProps定义默认参数

  • 定义props避开react关键字及保留字,常用的props及state定义可参考下表

  • 尽量少或者不使用ref获取和操作dom节点,使用state和prop进行控制dom

  • 事件调用使用在元素上onClick调用

  • 注意,react和html的表单元素的差异

  • 使用es6后,不支持mixin,使用decorator进行扩展,(babel?需要增加解析器)和高阶组件方式扩展。

  • 尽量不使用比较大的第三方js库

  • 组件方法定义顺序 constructor --> 声明周期方法(componentWillMount,componentDidMount,
    componentWillUpdate,componentDidUpdate,componentWillUnmount)

  • 尽量多而有用的代码注释,方法用块级注释,结构如下例。

  • 有必要需要些组件的销毁方法,比如 定时器,需要用销毁方法销毁定时器

  • ...others 没有必要 勿用

  • 自身定义的props属性应避免与react的关键字相同

  • 代码规范使用 airbnb规范

样式规范

  • 组件样式使用sass编写,公用样式使用tinper-bee-core包,请阅读tinper-bee-core文档
  • 组件样式调用,使用classnames模块,进行样式处理,使用className调用
  • 所有组件默认类名命名以`u-``开头
  • 组件使用clsPrefix为样式前缀,用户也可在组件上设置自定义的clsPrefix="yourPre"
const clsPrefix = 'u-select';
const class1 = {
    [`${clsPrefix}-item`]: true,
    [`${clsPrefix}-item-last`]: stepLast,
    [`${clsPrefix}-status-${status}`]: true,
    [`${clsPrefix}-custom`]: icon
  };
const class2 = [`${clsPrefix}-submit`, `${clsPrefix}-item`];

const classString = classNames('hide', class1, class2);
  • 可提供多种颜色的组件,在写scss文件时,颜色设置需提出公用的minxin方法例如Alert组件中设置多种颜色: @alert-styles-variant

通用组件接口规范

参数 说明 类型 默认值
size 尺寸 string medium
color 颜色 string ''
shape 形状 string ''
disabled 是否禁用(disabledtrue false) bool false
className 增加额外的类名 string ''
htmlType html dom 的 type 属性 string ''
style 内联样式 object ''
clsPrefix 自定义样式前缀 string ''

对于方法的传递,外部使用onClick传入事件,内部使用handleClick进行接收使用

国际化

当你的组件包含一些文字时,在src目录下,创建i18n.js文件,写下对应的hash值。内容如下:

module.exports = {
    'zh-cn': {
        'ok': '确定',
        'cancel': '取消',
        'isee': '知道了'
    },
    'en-us': {
        'ok': 'ok',
        'cancel': 'cancel',
        'isee': 'ok'
    }
}

组件内这么使用:

import i18n from './i18n';
import React from 'react';

Example.defaultProps = {
    locale: 'zh-cn'
};

class Example extends React.Component {
    constructor(props) {
        super(props);
    }


    render() {
        const {locale} = this.props;
        const locale = i18n[locale];

        const buttons= [
            <Button>
                {locale['ok']}
            </Button>,
            <Button>
                {locale['cancel']}
            </Button>
        ];
        return (
            <div>
            { buttons }
            </div>
            )
    }
}


基本代码结构

//引入依赖
import React from 'react';
import ReactDOM from'react-dom';
import classnames from 'classnames';

//定义prop检验
const propTypes = {
//每一个props都要写注释
}

//定义默认参数
const defaultProps = {

}

/**
 * 定义组件
 */
class Button extends React.Component {
	constructor (props) {
		super(props);
		//定义state
		this.state = {
    //每一个state要写注释
		}
		// 事先声明方法绑定
		this.MyEvent = this.MyEvent.bind(this);
	}

    //自定义函数方法,及注释
      MyEvent () {

      }
    //组件生命周期方法


	render () {
		return (
			// <div onClick={this.MyEvent}></div>
		)
	}
}

Button.propTypes = propTypes;
Button.defaultProps = defaultProps;

export default Button;

常用npm包

keyCode
warning
var warning = require('warning');

var ShouldBeTrue = false;

warning(
  ShouldBeTrue,
  'This thing should be true but you set to false. No soup for you!'
);
bee-animate
bee-overlay
dom-helpers (3.0.0)

参考链接
https://github.com/react-component/react-component.github.io/blob/master/docs/zh-cn/code-style/js.md

规范的落地:编辑器配置和构建流程集成

编辑器配置文件

.editorconfig 文件:

## http://editorconfig.org
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true

[*.md]
max_line_length = 0
trim_trailing_whitespace = false

[COMMIT_EDITMSG]
max_line_length = 0

eslint、csslint

可采用开源方案 eslint 和 csslint ,将其集成在 webpack、gulp 等构建工作流中。后续 uba 将会在工具层结合规范进行封装。

资源:

性能优化相关方案及规范

以下为前端性能优化相关的方案指导,具体优化实践应结合项目具体情况进行,不可为了优化而优化(性能优化和开发效率、用户体验以及实现成本等是相结合的)。性能优化的实现手段多样,关注领域复杂,从前到后,自上而下,方方面面都是性能优化的地方。

图片优化

  • 优化图像

    • 检查gif图片图像颜色数量和调色板规格是否一致
    • 有gif的情况考虑换成png
    • 色值较少的图片切成jpg
  • Inline images

    • 通过编码的字符串将图片直接内嵌在网页文本中
    • base64编码
  • image Maps

    • 将多张图拼在一起,通过坐标来控制显示导航
  • 优化css sptite,合成雪碧图

    • 可以选用单独的工具来生成雪碧图
    • 使用构建工具来分析产出雪碧图
  • 不在html中缩放图片

  • 使用小且可缓存defavicon.ico

    • 浏览器总会去请求这个图标
    • 确保图标存在,否则报错
    • 文件尽量小,最好小于1K
    • 设置一个长的过期时间

对 javascript 脚本代码的优化

  • js脚本文件置底
  • 现在主流浏览器都支持defer关键字,可以指定脚本在文档加载后执行
  • 使用外链js和css文件
  • 减少DOM访问和查询
    • 换成已经访问过的元素
    • 优化dom的选择器
    • 避免通过js修复layout
    • 优化逻辑减少dom操作
    • 双向数据绑定
    • dom diff
    • dom拼接
  • 减少或避免引起页面repaint或reflow的代码逻辑
  • 使用智能事件处理方式
    • 事件代理

对 css 的优化

  • 样式表置顶
  • 避免css表达式
  • 用link代替@import
  • 减少层级
  • 避免filters
  • 避免冗余
  • css文件合理管理

关于 cookie 的使用规范

  • 减少cookie大小
  • cookie用来做认证和个性化设置,在请求中包含在http报文头
  • 如果网页不需要用cookie,就完全禁掉
  • cookie的domain不可影响到子domain
  • 设置合适的过期时间
  • 页面内容使用无cookie域名

在服务器端的一些优化手段

  • 使用cdn分发资源
    • cdn通过部署在不同地区的服务器来提高客户的下载速度
    • 可将站点大量的静态内容放上cdn
  • 添加expires或cache-control报文头
    • 静态内容添加expires,将静态内容设置为永不过期
    • 动态内容适合cache-control,让浏览器根据条件来发送请求
  • gzip压缩
  • 配置etags
  • flush输出
  • get请求
  • 避免空的图片src

对于网页资源的优化

  • 减少http请求次数
    • combo合并多个文件请求为一个
    • 打包合并文件,打包策略很重要
  • 减少dns查询次数
    • 愿意在于网页中包含不同domain的内容时,如嵌入广告或引用了外部图片或脚本导致
    • dns查询结果可缓存在本地和浏览器一段时间,所以一般在首次访问时消耗流量
  • 避免页面跳转
  • 缓存ajax请求
  • 延迟加载
    • 确定页面初始加载需要的最小内容集,剩下内容推到延迟加载的集合中
  • 提前加载
    • 有条件提前加载
      • 用户的输入推断需要加载的内容,如智能搜索框
    • 无条件提前加载
      • 当前网页加载完成后,马上去下载一些其他的内容,这些内容是必须会用到的或是公共依赖的
  • 减少DOM元素数量
  • 根据域名划分内容
  • 减少iframe数量
    • 优点
      • 用来加载速度较慢的广告
      • 安全沙箱保护
      • 脚本可以并行下载
    • 缺点
      • 即使iframe内容为空也消耗加载时间
      • 会阻止页面的加载
  • 避免404

移动端方面的优化

  • localStorage本地存储与优化
    • 大数据量交互,数据不怎么更新的,含版本控制机制,一次请求,之后高枕无忧
    • 充分利用其资源
  • 保持单个内容小于25K
    • 这是因为iphone的限制,它只能缓存解压后小于25K的资源
    • 所以单纯gzip不一定够用,精简文件工具
  • 打包文档

参考链接
https://github.com/iuap-design/YY-Code-Guide