说明一些基础事项,并作为其他规范的准则。
- 风格统一,所有代码应该看上去像出自同一人之手。
- 易于理解,代码应当能够自解释,并有足够的文档。
- 提高质量,提供最佳实践,避免常见和易犯的错误。
类似 RFC 2119,文档中出现的要求级别的定义如下,Code Review 遵照此标准。文档中不应该出现其他表示要求的词汇。
- 必须:表示绝对要求这样做。
- 禁止:表示绝对不要求这样做。
- 应该:表示一般情况下应该这样做,但是在有正当和充分的理由时可以忽视这个要求。
- 不应该:表示一般情况下不应该这样做,但是在有正当和充分的理由时可以忽视这个要求。
- 可以:表示这只是个建议,是完全是可选的。你可以这样做,也可以不这样做。
-
Code Review
- 通过 github 提交记录进行
- 每周轮流由两位前端组成员分模块负责 review 工作
- 发现的问题要求下次 review 之前完全改正
-
eslint 等工具 要求工具审查通过才能提交到远程仓库
- IE 系列:IE10 及以上
- Chrome 内核/Firefox/Safari:国内占比 1% 以上的版本
- 其他主流国产套壳浏览器的 Chrome 内核模式
- 静态页面(尤其是首页和注册页面):IE8 及以上
- _l()方法中禁止嵌套_l()方法
- 文案尽量不要断词,中间有变量的用%0、%1依次追加上去
适用 Web 前端的 JavaScript 代码规范。在进行 Web 前端 JavaScript 编码时应该遵守此规范。
-
文件中缩进必须统一。新文件应该使用 2 空格缩进,禁止使用 tab 符号。老代码应该遵循原来的缩进
-
语句都必须有分号结尾,除了for, function, if, switch, try, while
某些情况下如果不注意,漏掉分号可能会产生意料之外的结果
// bad MyClass.prototype.myMethod = function() { return 42; } (function() { // 一些代码 })(); var x = { 'i': 1, 'j': 2 } [normalVersion, ffVersion][isIE](); var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] -1 == resultOfOperation() || die();
-
代码块和方法的左大括号应该放在上一行行末,而不是另起一行
// bad function CSharpStyle() { }
// good function JSStyle() { }
-
如果通过 if 和 else 使用多行代码块,应该把 else 放在 if 代码块关闭括号的同一行。try/catch 同理
// bad if(a > b) { // 一些代码 } else if(a < b) { // 另一些代码 }
// good if(a > b) { // 一些代码 } else if (a < b) { // 另一些代码 }
-
所有的循环体和判断体必须使用大括号括起来,即使只有一行
如果不注意可能会导致代码与想法不符,并可能导致空悬 else 问题
-
每行不应该超过 120 个字符
便于在不左右滚动屏幕的情况下快速阅读代码
-
运算符前后、左大括号前、逗号后应该有一个空格。行尾不应该有空格
-
在对象和数组的声明中,逗号应该放在成员后而不是下一行。如果所有成员写在同一行,最后一个成员后不应该加逗号;否则最后一个成员后应该添加逗号
最后一个成员添加逗号可以减少合并冲突
//good var obj = {a: 1, b: 2}; var obj = { a: 1, b: 2, };
-
长方法调用折行时,应该将 . 放在下一行。进行链式调用时,可以使用一定的缩进表示调用的对象和层级
//good $('body').hide() .append('div') .find('img') .remove() .end() .show();
-
比较长的标识符或者数值, 不应该为了让代码好看些而手工对齐
//bad var a = 1; var aLongVariable = 2;
//good var a = 1; var aLongVariable = 2;
-
应该使用空行来划分一组逻辑上相关联的代码片段
-
声明变量必须加上 var 关键字
-
变量名必须含义明确,不应该使用单字母或难以理解的变量名。含义十分明确的情况下,短循环中可以使用实体首字母命名变量。应该避免错误的单词拼写,对已有的单词拼写错误在可控的情况下应该进行纠正
-
普通(非模块)变量名、对象的属性名、非构造函数的方法名必须使用小驼峰(camelCase)命名法
var variable; var obj = {a: 1}; function someFunc() {}
-
类和构造函数必须使用大驼峰(PascalCase)命名法;React 组件应该使用大驼峰命名法;模块名可以使用大驼峰命名法
function Constructor(){} var inst = new Constructor();
-
常量/枚举名必须使用全大写,单词间以下划线分隔
var CHAT_SERVER = '';
-
不应该暴露私有变量。外除 lodash 库和用来保存 this 引用的 _this 外,变量和属性名禁止以下划线开头
-
保存 this 的引用时,变量名应该使用 this 所指代对象的有含义的名字。如果实在很难表达,可以使用 _this、self 或 that
//not so good posts.forEach(function() { var _this = this; //... });
//good posts.forEach(function() { var post = this; //... });
-
应该使用 var 声明每一个变量,并放在单独的行中
减少合并冲突
//bad var a = 1, b = 2;
//good var a = 1; var b = 2;
-
应该在作用域顶部声明变量
-
可以给函数命名
-
不应该重新定义同名变量
-
应该使用原始值,不应该包装基本类型
//bad var box = new String('原始值');
-
应该使用字面值创建对象
//bad var obj = new Object();
//good var obj = {};
-
禁止使用保留字作为键,应该使用同义词(而不是同音词)替换
//bad var obj = {class: 'post'}; var obj = {klass: 'post'};
//good var obj = {type: 'post'};
-
应该使用 . 访问对象的属性,除非是通过变量,或键名中有特殊字符
//bad var a = obj['a'];
//good var a = obj.a; var a = obj[a]; var b = obj['member b'];
-
应该使用字面值创建数组
//bad var arr = new Array();
//good var arr = [];
-
应该使用 push 方法向数组添加元素,而不是直接赋值
//bad arr[arr.length] = 1;
//good arr.push(1);
-
应该使用 slice 方法进行数组拷贝,在 ES6 中应该使用 ...
//good var clone = arr.slice(); //good(ES6) var clone = [...arr];
-
应该使用 slice 方法将类数组对象(如 arguments)转换为数组
-
应该使用单引号表示字符串,除非字符串里有单引号
//bad var str = "How are you";
//good var str = 'How are you'; var str = "I'm fine";
-
长字符串可以使用 + 号连接并进行换行
+ 号连接字符串可读性比数组合并或 += 更高,并且在现代浏览器中性能更优
-
对于 HTML 模版,应该使用模版引擎或 ES6 模版字符串进行渲染,而不是拼接字符串。3 行以下包括变量、10 行以下不包含变量的 html 拼接不要求使用模版引擎。
-
应该使用 doT 模板引擎,并使用默认语法(使用默认的
{{
和}}
,流程控制应该尽可能使用模板引擎自带的语法而不是 JS 语句) -
如果一定要拼接 HTML 字符串,应该注意对变量进行 HTML 实体转码
//bad var html = '<div>' + htmlEncode(data.content) + '</div>'; //bad var html = doT.template('<div><%= it.content%></div>')(data);
//not so good var html = `<div>${htmlEncode(data.content)}</div>`; //good var html = doT.template('<div>{{! it.content}}</div>')(data);
-
禁止将函数声明放在非函数代码块(if、while 等)中,而是应该将其赋给一个变量
//bad if(a > 0) { function someFunc() {} someFunc(); }
//good if(a > 0) { var someFunc = function someFunc() {} someFunc(); }
-
禁止将参数命名为 arguments
-
形参过长时,可以使用单个对象传递参数。此时应该为每个参数添加详尽的注释
-
函数返回多个值时,应该使用对象而不是数组
//bad function multiReturnValue() { return [1, 2]; }
//good function multiReturnValue() { return {a: 1, b: 2}; }
-
可以使用函数声明代替函数表达式
抛出异常时可以比较容易追踪到方法名
// ok var someFunc = function() {}; //good function someFunc() {}
-
函数可以返回 this 来方便链式调用。jQuery 组件应该返回 this
-
使用闭包时应该注意避免产生循环引用
//bad function foo(element, a, b) { element.onclick = function() { /* uses a and b */ }; }
//good function foo(element, a, b) { element.onclick = bar(a, b); } function bar(a, b) { return function() { /* uses a and b */ } }
- 可以自定义一个 toString 方法。自定义的 toString 方法不应该有副作用或抛出异常
- 在 ES6 中应该使用 class 代替构造函数。应该使用 extends 关键字进行继承
-
应该使用 === 和 !==,避免使用 == 和 !=,除非是老代码/接口不确定类型
-
应该了解不同类型的布尔类型转换方式,在 if 和三元运算符中使用较短的表达方式
-
应该避免不必要的判断代码
-
禁止在判断语句中进行赋值操作。应该避免 yoda 表达
//bad if (a = 1) {} if (1 === a) {} if (a === 0) {} function noA() { if(a) { return true; } else { return false; } }
//good if (a === 1) {} if (!a) {} function noA() { return !!a; }
-
不应该使用 ~ 运算符进行 indexOf 的结果判断
不够直观
//bad if(~list.indexOf(item)) {}
//good if(list.indexOf(item) !== -1) {} if(list.indexOf(item) >= 0) {}
-
禁止对内置对象进行原型修改
-
应该避免使用老代码中顶级对象的原型扩展方法,使用工具方法代替
-
应该优先使用工具库(lodash,jQuery)中的方法,缺少相关方法时再自行实现
//bad String.prototype.trim = function(str) {...} str = str.trim();
//good str = _.trim(str); str = $.trim(str);
-
工具类和公共方法应该放在公共文件中。修改公共文件后必须告知全员
- 应该遵守严格模式,即使没有使用 'use strict' 语句。可以使用 'use strict' 显式开启严格模式
- 除底层工具库外,禁止使用 eval([string]), new Function([string]), setTimeout([string], …) 以及 setInterval([string], …) 等直接执行字符串的方式
- 如无必要,应该避免引入 window/global 上的全局变量
> 为启用 CSP 提供可能
-
应该将 JavaScript 标签都放在外部文件中进行引用
-
不应该内部引用 JavaScript
-
禁止内联引用 JavaScript
//forbidden <img onclick="javascript:someFunc(this);"> //bad <script>$(img).on('click', someFunc);</script>
//good <script src="bindEvents.js"></script>
要求后端返回的数据遵守下列规范
-
后端返回数据必须是 JSON 格式,并遵循变量命名规范
-
如果数据表示特定的类型,则它必须是对应的类型而不是字符串
//bad {"allowDown": "ok","commentCount":"1"}
//good {"allowDown": true,"commentCount":1}
-
除现有某些接口外,后端应该返回未编码过的数据,由前端进行编码。前端应该假设后端返回的数据都是不安全的
-
jQuery 对象的变量名应该使用 $ 开头
-
应该缓存 jQuery 选择结果,或使用链式调用避免不必要的元素查询
//bad $('div').show(); $('div').hide();
//good var $div = $('div'); $div.show(); $div.hide(); //good $('div').show().hide();
-
应该避免无用的选择。如果不需要用到父元素,应该使用单个选择器一次选中目标元素
//bad $('.parent #id').hide();
//good $('#id'); //good var $parent = $('.parent'); // do sth. with $parent $parent.find('#id').hide();
-
如果需要在重复元素上绑定相同或类似的事件,应该使用事件委托,绑定在共同的父元素上
//bad $('ul li').on('click', function() {});
//good $('ul').on('click', 'li', function() {});
-
绑定到 #container 或更上层元素(body、document、window 等)上的事件必须有表示对应模块的命名空间
//bad $('body').on('click', function() {});
//good $('body').on('click.feed', function() {});
-
在事件中传递数据时,应该使用对象而不是原始值
方便后期修改
$(document).on('CUSTOM_EVENT', function(evt, arg) {}); //good $(document).trigger('CUSTOM_EVENT', {a: 1}) //bad $(document).trigger('CUSTOM_EVENT', 1)
-
不应该直接拼接 ajax 请求参数,应该将其作为参数传给 jQuery。如果一定要手工拼接参数,需要注意使用 encodeURIComponent 进行转码
//forbidden $.get('ajaxpage.aspx?a=' + a); //bad $.get('ajaxpage.aspx?a=' + encodeURIComponent(a));
//good $.get('ajaxpage.aspx', {a: a});
-
通过类名选择元素时应该添加有意义的钩子类,而不是样式类
//bad // <a class="ThemeColor3">动态</a> $('a.ThemeColor3').addClass('highlight');
//good // <a class="headLink ThemeColor3">动态</a> $('a.headLink').addClass('highlight');
-
操作 dom 元素时,可以尽量批量修改、通过 display:none 隐藏元素后进行操作以减少重绘和重排列
-
如无必要(如不存在兼容性问题且操作很简单),可以避免使用 jQuery
-
const > let > var,应该尽量使用 const 和 let 代替 var;相同的声明方法应该放在一起
-
声明对象中的属性和方法时应该使用简写,并将属性的简写放在前面
//bad var a = 1; var obj = {a: a, b: 2}; var obj = {b: 2, a};
//good var a = 1; var obj = {a, b: 2};
-
可以使用对象和数组的解构赋值
//good var {a, b} = obj;
-
应该避免使用 arguments 对象,而是使用 ... 语法
//good function(...args) { args.forEach(arg => console.log(arg)); }
-
应该使用参数默认值,而非在方法中判断
//bad function(a, b) { b = typeof b === 'undefined' ? 1 : b; }
//good function(a , b = 1) {}
-
使用参数默认值时应该避免副作用
-
使用函数表达式时,应该尽量使用箭头函数,在方法体不长时应该省略大括号和 return
//good callback(obj => console.log(obj));
待定
-
应该使用 jsdoc 格式进行注释,每个模块、方法声明、方法参数、公开变量、上都应该有注释。jQuery 参数暂时使用单行注释
-
应该使用 // 进行单行注释
-
应该使用 FIXME 表示问题,TODO 表示解决方案或代办事项
//good //FIXME: someFunc 未定义时会出异常 someFunc(); //TODO: 应该先判断 someFunc 是否定义 someFunc();
-
每个文件中的缩进必须统一。应该使用 4 空格缩进,老代码应该遵循原来的缩进
-
除 br、 hr、 img、 input、 link、 meta 外,其他标签必须闭合
-
属性应该使用双引号
//bad <a href='http://mingdao.com'></a>
//good <a href="http://mingdao.com"></a>
-
每个页面 html 结构必须完整,必须有 title 及合适的 meta 信息(列表待定)
-
css link/style 标签应该写在页头,script 标签应该写在页尾
-
每个文件中的缩进必须统一。应该使用 2 空格缩进,老代码应该遵循原来的缩进
-
类名应该使用小驼峰命名法则。如果使用 BEM 命名法,Block 可以使用大驼峰命名,Block 和 Element 之间使用
-
连接,Element 和 Modifier 之间使用--
连接。禁止使用下划线/* good */ .mdDialog .confirmButton.disabled {} /* good */ .MdDialog-confirmButton--disabled {}
-
类名应该含义明确,命名应该显示出语义而不是具体的呈现形式
/* bad */ .red { color: red; }
/* good */ .error { color: red; }
-
不应该使用 id 选择器,老代码可以不用修改
id 选择器优先级最高可能引起不必要的问题,并且不可重用
-
应该避免使用通用选择器 *
-
可以避免使用子选择器 >
它的性能优于后代选择器,但会造成与 html 间的高耦合
-
选择器原则上不应该超过 4 级(Less 中嵌套不超过 3 层)
-
老代码不应该使用工具格式化代码。如果需要调整,手工调整
-
记住匹配规则是从右往左,避免将效率低的选择器写在右侧
-
规则声明的左大括号 { 后应该换行,前面应该加上一个空格
-
规则声明的右大括号 } 应该独占一行
-
分割选择器的逗号前不应该有空格,后面应该加上一个空格或换行
/* good */ .shortNameA, .shortNameB { ... } /* good */ .reallyReallyLongNameA, .reallyReallyLongNameB { ... }
-
每个属性必须写在单独的一行,冒号后面空一格
/* bad */ body {font-size:12px;}
/* good */ body { font-size: 12px; }
-
每个模块应该有顶级 class 作为命名空间
/* bad */ .message{ float:left; }
/* good */ .mdAlertDialog .message{ float:left; }
-
如非特殊需要,不应该使用行内样式和 style 标签,应该使用外联 css 文件
-
编写代码时必须使用无浏览器前缀的写法
构建流程会自动加上合适的浏览器前缀
-
目前通用样式应该写入 basic.css(基础样式) 和 inStyle.css(登录后基础样式中)
待定
- 纯色图标应该优先使用 icon font
- 应该使用合适尺寸的图片并进行压缩,在质量和尺寸间合理取舍。禁止直接使用未压缩的原图或尺寸明显超过需要的图片
- 对于频繁出现的大量小图片,应该使用 CSS Sprite
- 每个提交功能应该单一
- 提交信息必须可读、有明确含义并说明此次提交的内容。可以遵守一些规范的格式
- 禁止将临时调试代码提交到代码库,如一些不必要的 console.log