总体原则

  1. 驼峰命名camelCase):用于定义 JavaScript基本数据类型函数方法
  2. 帕斯卡命名PascalCase):用于 JavaScript 声明Class类Object对象Array数组引用数据类型。
  3. 短横线命名kebab-case):用于自定义HTML视图SCSS样式Assets资源,即样式与视图相关的自定义元素都采用该方式命名。
<div>
  <main id="app">
    <section class="article-content">
      <my-paragraph>kebab-case</my-paragraph>
    </section>
  </main>
  <template>
    <style>
      #app {
        background: url(../assets/banner-logo.png); // kebab-case
      }
      .article-content {
        font-size: 18px; // kebab-case
      }
    </style>

    <script>
      let currentDate = "Tue Oct 10 2017 17:52:04 GMT+0800 (CST)"; // camelCase

      const changeDate = function () {
        console.log(new Date()); // camelCase
      };

      const YourName = {
        name: "Hank", // PascalCase
      };
    </script>
  </template>
</div>

所有代码缩进必须使用 2 个空格,并优先使用单引号',除非字符串嵌套需要,否则禁止单-双引号混用

JavaScript & ES6

适用于使用 Babel 提供 ES6 预编译环境的场景。

命名原则

代码块的花括号{、流程控制语句的小括号(前必须放置1 个空格,且每个函数、代码块之前通过换行进行分隔。

class Hank {
  constrocter() {
    (this.height = "182cm"), (this.weight = "75kg");
  }

  // 换行分隔代码块
  toString() {
    // 花括号前放置空格
    if (this.height && this.weight) {
      // 小括号前放置空格
      console.info("toString");
    }
  }
}

不要在函数参数列表的前后添加任何空格,但是可以使用1 个空格将 JavaScript 运算符分隔开,并且在每个代码文件末尾保留1 个空行

// 函数参数列表前后不添加空格
((window) => {
  var self = window; // 使用1个空格分隔运算符
})(window);
// 保留1个空行

使用美元符$作为存储jQuery 对象变量名称的前缀。

const menu = $("#menu"); // bad way

const $menu = $("#menu"); // good way

使用下划线_作为代码中私有变量的前缀,避免其它小伙伴的操作对该变量造成污染。

function traverse(array) {
  let _index = 0; // 将数组索引声明为私有变量,防止误操作导致索引泄露
  for (_index; array.length < _index; _index++) {
    console.log(array[_index]);
  }
}

严禁在项目中使用单个字母和拼音命名的变量和函数名称

数据类型

  • 基本数据类型: String、Number、Boolean、null、undefined,使用let进行定义,不再使用 var。
  • 引用数据类型:Object、Array、Function,引用数据类型全部通过const进行定义。
let myBoolean = true;
let myNumber = 32;
let myString = "this is a string";

const array = [0, 1, 2, 3, 4, 5];
const object = { a: "a", b: "b", c: "c" };

let 和 const 都具有代码块级的作用域,书写时注意将两者进行分组。

对象

使用字面量语法创建对象,而不要使用new关键字。

const myObject = new Object(); // not recommend

const myObject = {}; // it is correct

对象当中的方法使用简写函数简写属性进行定义。

let username = "admin";
let password = "admin";

vm.login = {
  username,
  password, // 简写属性
  // 简写函数
  auth(username, password) {
    // ... ...
  },
};

当对象中同时存在简写属性普通属性时,需要将简写属性写到单独的组。

vm.group = {
  // 简写属性
  username,
  password,
  isAuthorized,
  // 普通属性
  age: "20",
  height: 182,
  weight: 76,
  homeland: "CHINA",
};

使用.运算符访问对象属性,或者使用[]通过变量访问属性。

const user = {
  height: 182,
  weight: 75,
};

console.info(user.height);
console.info(user["weight"]);

ES6 中新出现的class关键字,实质是构造函数创建对象的一种糖衣语法,目的是更加容易的向对象添加原型方法。因此,建议尽可能使用class创建类及原型方法,避免在构造函数上使用prototype关键字。

//定义类
class Point {
  // 构造函数
  constructor(x, y) {
    this.x = x; // this指向实例对象
    this.y = y;
  }

  // 定义类方法不需要使用function关键字进行声明
  toString() {
    return this.x + this.y;
  }
}

通过在class类方法上返回this对象,可以在该类的实例化对象上进行链式方法调用。

class Hank {
  constructor(name, age) {
    this.age = age;
    this.name = name;
  }

  printName() {
    console.info(this.name);
    return this;
  }
  printAge() {
    console.info(this.age);
    return this;
  }
}

const hank = new Hank("uinika", 18);
hank.printName().printAge();

代码中尽可能使用extends实现继承,因为extends是 ES6 内建的**原型继承方法,不会对instanceof()的返回结果形成破坏。

class Uinika extends Hank {
  constructor(height, weight) {
    super("uinika", 18); // 实例化父级构造器
    this.height = height;
    this.weight = weight;
  }

  print() {
    console.log(this); // 打印自己以及父级构造器当中的属性
  }
}

数组

  • 使用字面量语法创建数组,而不要使用new Array()关键字。
const myArray = new Array(); // not recommend

const myArray = []; // it is correct
  • 添加数组元素时使用Arrary.push(),而不要使用索引赋值。
const myArray = [];

myArray[myArray.length] = "uinika"; // not recommend

myArray.push("uinika"); // it is correct
  • 使用 ES6 的扩展运算符(spread...复制数组。
const target = ["A", "B", "C", "D"];
const copy = [];

// not recommend
for (let index = 0; index < target.length; index++) {
  copy[i] = target[i];
}

// it's correct
const copy = [...target];
  • 使用 ES6 提供的Array.from()方法将类数组对象同时具备索引和长度)转换为数组。
const myObject = {
  "0": "A",
  "1": "B",
  "2": "C",
  length: 3,
};

let myArray = Array.from(myObject); // ['A', 'B', 'C']

字符串

为避免频繁按下shift+'组合键,请在代码中尽可能使用单引号'声明字符串。

const name = "Hank"; // bad way
const name = "Hank"; // good way

字符串过长时,必须通过+或者\运算符进行换行缩进处理,让代码更加美观易读。

// bad way
let bad = "hank is first name and zheng is last name";

// good way
let good =
  "hank is first name \
            and zheng is last name";

// best way
let best = "hank is first name" + "and zheng is last name";

拼接字符串HTML 模板时,请使用模板字符串${},而非字符串连接符+

// bad
function sayHi(name) {
  return "How are you, " + name + "?";
}

// good
function sayHi(name) {
  return `How are you, ${name}?`;
}

函数

使用函数声明(function declaration)代替函数表达式(function expression),因为前者可以进行函数提升(function hoisting)。

// bad
const bad = function () {};

// good
function good() {}

需要使用函数表达式匿名函数的场景下,请直接使用箭头函数符号=>,这样可以在箭头函数内部创建新的this作用域。

// 一个标准的箭头函数写法
(name, password) => {
  console.info(name, password);
};

// 箭头函数的简写形式
(name) => name + "zheng";

使用箭头函数来书写立即调用的函数表达式IIFE,Immediately Invoked Function Expression)。

(() => {
  console.info("Hello Hank!");
})();

布尔运算

尽可能使用===!==去同时比较数据类型,而非通过==!=仅比较

if (variable1 === "" && variable2 !== 0) {
  console.info("just a test!");
}

各数据类型的布尔运算结果如下,使用if()等逻辑判断语句时需要注意。

  1. 对象类型的objectarray都计算为true,无论其是否为空对象{}或者空数组[]
  2. 字符串类型''计算为false,但非空字符串'string'计算为true
  3. 数字类型NaN0计算为false
  4. undefinednull都是false
if ([0]) {
  // 结果为true,因为JavaScript中object和array都同属对象类型,布尔运算时对象类型都会被渲染为true。
}

利用 JavaScript 逻辑判断语句的强制数据类型转换特性,可以更加简洁的书写布尔判断。

if (name !== "") {
} // bad
if (name) {
} // good

if (collection.length > 0) {
} // bad
if (collection.length) {
} // good

如果通过if...else...语句使用多行代码块,则else放在if代码块关闭括号}同一行。

// bad job
if (test) {
  ...
}
else {
  ...
}

// good job
if (test) {
  ...
} else {
  ...
}

注释

  • 使用/** */作为多行注释,用来标注全局功能模块的名称类型参数返回值描述等信息。
/**
 * @name   auto-resize
 * @type   directive
 * @param  数值类型,表示缩进的像素值
 * @return null
 * @description 自动根据当前window大小计算页面的显示尺寸
 */
  • 使用/* */作为单行注释,标注局部代码块的参数返回值描述信息
/*
 * @param  用户输入字符串
 * @return null
 * @description 去除目标字符串全部空格
 */
function trim(input) {
  if (typeof input === "string" && input) input.replace(/\s/g, "");
}
  • 使用//作为行内注释,标记代码段信息。
function trustHtml($sce) {
  return $sce.trustAsHtml(val); //返回被信任的HTML字符串
}

使用// TODO:格式注释描述问题本身,使用// FIXME:格式注释描述问题解决方式

() => {
  issue(); // TODO: 描述问题本身的信息
  handle(); // FIXME: 描述如何解决问题
  return this;
};

解构赋值

通过解构赋值存取多属性对象,可以减少临时变量声明的数量。

/* 对象的解构赋值 */
function getFullName1(user) {
  const name = user.name; // bad way
  const password = user.password;
  return `${name} ${name}`;
}
function getFullName2(user) {
  const { name, password } = user; // good way
  return `${name} ${password}`;
}
function getFullName3({ name, password }) {
  return `${name} ${password}`; // best way
}

/* 对象的解构赋值 */
const array = [1, 2, 3, 4];
const array1 = array[0]; // bad way
const array2 = array[1]; // bad way
const [array1, array2] = array; // good way

函数返回值时,直接返回对象{}而非数组[],避免因为数组索引改变导致不能正确读取相应位置数据。

CSS & SCSS

命名原则

  • reset.scss:消除默认样式和浏览器差异,并设置部分标签的初始样式,例如<html><body>的宽高度的100%
@import "./base";
@import "./color";

.reset {
  margin: 0;
  padding: 0;
  border: none;
  outline: none;
  width: 100%;
  height: 100%;
}

html {
  @extend .reset;
  body {
    @extend .reset;
    font-size: 16px;
    font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
      "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
    #app {
      @extend .reset;
      .router {
        @extend .reset;
      }
    }
  }
}
  • color.scss:项目中页面的所有取色必须来自这个颜色变量列表,色表内部的变量以color-颜色-深度格式命名。
// infomation
$color-primary: $color-blue;
$color-success: #13ce66;
$color-warning: #f7ba2a;
$color-danger: #ff4949;
// scheme
$color-blue: #20a0ff;
$color-blue-light: #58b7ff;
$color-blue-dark: #1d8ce0;
$color-pink: #ff0097;
$color-cyan: #4eb3b9;
$color-black: #1f2d3d;
$color-black-light: #324057;
$color-black-light-extra: #475669;
$color-silver: #8492a6;
$color-silver-light: #99a9bf;
$color-silver-light-extra: #c0ccda;
$color-gray: #d3dce6;
$color-gray-light: #e5e9f2;
$color-gray-light-extra: #eff2f7;
$color-white: #ffffff;
$color-white-dark: #f9fafc;
  • base.scss:基础的公用快捷样式,通过 HTML 元素的 class 属性直接使用。
  • skin.scss: 全局 UI 插件的样式补丁,如果存在多种皮肤,则以skin-xx.scss方式进行命名。
  • grid.scss:自定义的 CSS 栅格系统,直接通过 HTML 元素及 class 属性使用。

每个 Vue 或者 React 根级组件的样式都独立到单独模块书写,禁止在顶层 ID 选择器之外再定义其它 CSS 样式,避免对全局样式形成污染。私有组件顶层 CSS 选择器使用id属性定义,公用组件的顶层选择器使用class属性定义,。

@import "../common/styles/base.scss";
#ID {
  // 一律使用单行注释
}

为了避免 SASS 中使用多行注释时,将注释内容打包至最终产品代码,因此非公用模块的注释一律使用单行注释//

布局选择器

语义命名简写
文档docdoc
头部headhd
主体bodybd
尾部footft
主栏mainmn
主栏子容器main-containermcc
侧栏sidesd
侧栏子容器side-containersdc
盒容器wrap/boxwrap/box

模块/组件选择器

语义命名简写
导航navnav
子导航subnavsnav
面包屑crumbcrm
菜单menumenu
选项卡tabtab
标题区head/titlehd/tt
内容区body/contentbd/ct
列表listlst
表格tabletb
表单formfm
热点hothot
排行toptop
登录loginlog
标志logologo
广告advertisead
搜索searchsch
幻灯slidesld
提示tipstips
帮助helphelp
新闻newsnews
下载downloaddld
注册registreg
投票votevote
版权copyrightcprt
结果resultrst
标题titlett
按钮buttonbtn
输入inputipt

功能选择器

语义命名简写
浮动清除clear-bothcb
向左浮动float-leftfl
向右浮动float-rightfr
内联块级inline-blockib
文本居中text-align-centertac
文本居右text-align-righttar
文本居左text-align-lefttal
垂直居中vertical-align-middlevam
溢出隐藏overflow-hiddenoh
完全消失display-nonedn
字体大小font-sizefs
字体粗细font-weightfw

颜色/背景选择器

语义命名简写
字体颜色font-colorfc
背景backgroundbg
背景颜色background-colorbgc
背景图片background-imagebgi
背景定位background-positionbgp
边框颜色border-colorbdc

状态选择器

语义命名简写
选中selectedsel
当前currentcrt
显示showshow
隐藏hidehide
打开openopen
关闭closeclose
出错errorerr
不可用disableddis

前端基础架构已经提供了基于 postcss 的后置处理器 autoprefixer(对前置 SASS 编译后的 CSS 进行再处理),因此编写样式时不再手动处理-webkit--moz-等兼容性前缀。

HTML

HTML 标签的语义化有助于形成构架良好的 DOM 结构,有助于搜索引擎优化和提升可访问性, 尽可能保持 HTML DOM 结构的优雅。

整体 DOM 结构

统一使用 HTML5 提供的<!DOCTYPE html>文档类型声明,并在<head>中使用<link>引入外部 CSS 文件,然后在<body>底部通过<script>引入 JavaScript 文件。

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <title>Aves</title>
    <meta name="renderer" content="webkit" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>

  <body>
    <div id="app">
      <main id="dashboard"></main>
    </div>
  </body>
</html>

组件 DOM 结构

每个 Vue 或者 React 根级组件的顶层元素(通常是指全局的路由视图)一律通过<main>元素定义,因为同一个文档中<main>标签只可以出现一次,自定义组件一律使用<div>进行定义,id属性命名则使用短横线连接的router-component格式。

<!-- 全局路由视图 -->
<main id="router">
  <!-- 组件 -->
  <div id="router-component"></div>
</main>