通用注释规范

在开发过程中会用到各种各样的配置文件, 但配置文件的规范可能有较大的不同, 这里对规范进行统一的说明

以下文档使用 全屏模式观看, 否则可能会出现样式错乱

Nginx / Env

支持 # 类型的注释, Env 使用注释和 Nginx 注释相同, 采用 # 进行单行注释, 注释风格如下

1
2
3
4
5
6
# -----------------------------------------------------
# Long Comment
# -----------------------------------------------------

# short Comment
# -----------------------------------------

Centrifugo(.ini)

Centrifugo 均使用 ini 后缀, 配置 = 前后不允许存在空格

Php / Javascript / Css

支持 /*...*/ 类型的注释均使用此格式

1
2
3
4
5
6
7
8
9
10
11
// 长注释
/*
|------------------------------------------------------------------
|
|------------------------------------------------------------------
|
*/

// 短注释
/*
* ---------------------------------------- */

JavaScript - 编码规范 v1.0

1. 命名规则

使用含义丰富的名字

杜绝使用无意义的单字母命名

1
if (currentYear > 2009) ...

组成

命名应该由 26 个大小写字母(A .. Z, a .. z),10 个数字(0 .. 9), $(美元符号)_(下划线) 组成。不要使用国际字符(例如中文, emoji 表情),因为它们可能不易读或者不能在任何地方都能容易理解。不要使用 \(反斜线符号), $ 符号仅仅作为被选择的 dom 进行使用

下划线

不要使用 _(下划线) 作为变量名称的首字母。它被用来表示私有,但是它实际上不提供私有性。
文件或类中的 私有 属性, 变量和方法名应该以下划线 _ 开头.

首字母小写

大多数变量和方法名应该以小写字母开始。[变量/函数]杜绝使用拼音命名

类首字母使用大写

必须使用 new 前缀的构造函数应该以大写字母开始。JavaScript 不会在省略 new 时报编译期警告或运行时警告。
不使用 new 时会发生坏事情,所以大写首字母规范是我们拥有的唯一的防御。

常量使用大写

常量的形式如: NAMES_LIKE_THIS, 即使用大写字符, 并用下划线分隔. 你也可用 @const 标记来指明它是一个常量. 但请永远不要使用 const 关键词,关键词 const, 因为 IE 不能识别, 所以不要使用

变量声明

声明变量必须加上 var 关键字 . JavaScript 不强求这点,但是这样做会让程序更易读,并且会让探测未声明的可能变成隐式的 globals 的 变量更容易。
var 语句应该为方法体内的第一个语句。
每个变量声明应该自己占一行并有注释。它们应该按字母顺序排列。

1
2
3
var currentEntry; // currentyly selected table entry
var level; // indentation level
var size; // size of table

JavaScript 没有块作用域,所以在块里定义变量可能会让有其它 C 家族语言经验的程序员迷惑。在方法顶端定义所有变量。 尽量少使用全局变量。隐式的全局变量应该从来不使用。

方法声明

所有的方法应该在它们使用前声明。内部方法应该位于 var 语句后面。这让哪些变量包含在它的 scope 里更清楚。
方法体本身缩进 Tab。} (右大括号)应该和方法声明处对齐。

1
2
3
4
5
6
7
function outer(c, d) {
var e = c * d;
function inner(a, b) {
return (e * a) + b;
}
return inner(0, 1);
}

这个规范可以和 JavaScript 很好的工作,因为在 JavaScript 里方法和对象字面量可以放在允许表达式的任何位置。它使用内部方法和复杂结构提供最好的可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function getElementsByClassName(class_name) {
var results = [];
walkTheDOM(document.body, function (node) {
var a; // array of class names
var c = node.className; // the node's classname
var i; // loop counter
// If the node has a class name, then split it into a list of simple names.
// If any of them match the requested name, then append the node to the set of results.
if (c) {
a = c.split(" ");
for (i = 0; i < a.length; i += 1) {
if (a[i] === class_name) {
results.push(node);
break;
}
}
}
});
return results;
}

如果一个方法字面量为匿名的,则在“function”和“(”(左圆括号)之间应该有一个空格。如果省略空格,则它可能看起来方法名是 function”,而这是错误的。

1
2
3
4
5
6
7
8
9
div.onclick = function (e) {
return false;
};
that = {
method: function () {
return this.datum;
},
datum: 0;
};

尽量少用全局方法。

方法参数/入参

方法中的参数使用 蛇形写法, 这里的方法包含回调函数, 定义函数, 类方法

1
2
3
4
function getElementsByClassName(class_name) {
var results = [];
// ...
}

2. 文件及格式化

JavaScript 文件

JavaScript 程序应该作为一个.js 文件存储和发布。
JavaScript 代码不应该嵌入在 HTML 文件里,除非那些代码是一个单独的会话特有的。HTML 里的 JavaScript 代码大大增加了页面的大小,并且 很难通过缓存和压缩来缓解
<script src="filename.js"> 标签应该在 body 里越靠后的位置越好。这减少了由于加载 script 而导致的其它页面组件的延迟。没有必要使 language 或者 type 属性。由服务器而不是 script 标签来决定 MIME 类型。
文件名应该使用小写字符, 以避免在有些系统平台上不识别大小写的命名方式. 文件名以.js 结尾, 不要包含除 - 和 _ 外的标点符号(使用 - 优于 _).

继续缩进

当一条语句不能在单独一行写完时,可能有必要拆分它。在操作符后进行拆分,最好是在逗号后面拆分。
操作符后面进行拆分减少了通过插入分号伪装 copy-paste 错误的可能性。下一行应该缩进 + 4空格

注释

慷慨的写注释。留下一些供需要理解你做了什么的人们(可能是你自己)下次阅读的信息是有用的。注释应该书写良好和清晰,就像它们 标注的代码一样。偶尔小幽默一把也是可以的。挫折和怨恨就别写了。
更新注释非常重要。错误的注释让程序更难读懂和理解。
让注释有意义。更多的关注于不能马上可见的东西。不要用如下内容浪费读者的时间:

1
i = 0; // Set i to zero.

一般使用行注释。把块注释用于正式文档或外部注释。
修改的重要的注释使用 /* Comment (User)[2014-05-06] */ 来进行注释, 其余采用简洁注释

空格缩进

使用 空格 进行缩进, 使用正确的缩进来表明嵌套层次, 4 个空格作为一个缩进

文件编码为 utf-8

空行

空行通过将逻辑相关的代码放到一起来增加可读性。

空格

空格应该用于如下情况:

  • 关键字后面跟“(”(左圆括号)时应该用一个空格隔开。
  • 方法名和方法的“(”(左圆括号)之间不要有空格。这利于区分关键字和方法调用。
  • 所有的二元操作符,除了“.”(圆点)、“(”(左圆括号)和“[”(左中括号),都应该使用一个空格来和操作数隔开。
  • 一元操作符和操作数之间不应该使用空格隔开,除了操作符是一个单词时,如 typeof。
  • for 语句控制部分的每个“;”(分号)应该在后面跟一个空格。
  • 每个“,”(逗号)后面应该跟一个空格。

分号

总是使用分号.
如果仅依靠语句间的隐式分隔, 有时会很麻烦. 你自己更能清楚哪里是语句的起止.
而且有些情况下, 漏掉分号会很危险

数组和对象的初始化

如果初始值不是很长, 就保持写在单行上:

1
2
var arr = [1, 2, 3];  // No space after [ or before ].
var obj = {a: 1, b: 2, c: 3}; // No space after { or before }.

初始值占用多行时, 缩进 2 个空格.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Object initializer.
var inset = {
top: 10,
right: 20,
bottom: 15,
left: 12
};
// Array initializer.
this.rows_ = [
'"Slartibartfast" <fjordmaster@magrathea.com>',
'"Zaphod Beeblebrox" <theprez@universe.gov>',
'"Ford Prefect" <ford@theguide.com>',
'"Arthur Dent" <has.no.tea@gmail.com>',
'"Marvin the Paranoid Android" <marv@googlemail.com>',
'the.mice@magrathea.com'
];

比较长的标识符或者数值, 不要为了让代码好看些而手工对齐. 如:

1
2
3
4
5
WRONG_Object.prototype = {
a : 0,
b : 1,
lengthyName: 2
};

函数参数

尽量让函数参数在同一行上. 如果一行超过 80 字符, 每个参数独占一行, 并以 Tab 缩进, 或者与括号对齐, 以提高可读性. 尽可能不要让每行超过 80 个字符. 比如下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
// renaming without reindenting, low on space.
goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
// ...
};
// survives renaming, and emphasizes each argument.
goog.foo.bar.doThingThatIsVeryDifficultToExplain = function(
veryDescriptiveArgumentNumberOne,
veryDescriptiveArgumentTwo,
tableModelEventHandlerProxy,
artichokeDescriptorAdapterIterator) {
};

二元和三元操作符

操作符始终跟随着前行, 这样就不用顾虑分号的隐式插入问题. 如果一行实在放不下, 还是按照上述的缩进风格来换行.

1
2
3
4
5
6
7
8
var x = a ? b : c;  // All on one line if it will fit.
// Indentation +4 is OK.
var y = a ?
longButSimpleOperandB : longButSimpleOperandC;
// Indenting to the line position of the first operand is also OK.
var z = a ?
moreComplicatedB :
moreComplicatedC;

需要时候使用括号

不要滥用括号, 只在必要的时候使用它.
对于一元操作符(如 delete, typeof 和 void ), 或是在某些关键词(如 return, throw, case, new )之后, 不要使用括号.

字符串

使用 ' 优于 "
单引号 ' 优于双引号 ". 当你创建一个包含 HTML 代码的字符串时就知道它的好处了.

3. 语句

简单语句

每行应该包含至少一个语句。在每个简单语句末尾添加一个“;”(分号)。注意一个给方法字面量或对象字面量赋值的赋值语句仍然是一个赋值语句,所以也必须以分号结尾。
JavaScript 允许任何表达式作为语句使用。这可能产生一些错误,特别是在插入分号时。唯一可以当作语句使用的表达式是赋值表达式和调用表达式。

复合语句

复合语句是包含一个用“{}”(大括号)包围语句列表的的语句。

  • 包围的语句应该缩进 Tab。
  • “{”(左大括号)应该位于开始复合语句的行的末尾。
  • “}”(右大括号)应该新起一行并且和相匹配的“{”所在那行的起始位置对齐
  • 当语句是控制结构的一部分时,所有语句都应该用括号包围,即使是单行语句,例如 if 或 for 语句。这让添加语句更容易而且不会引起 Bug。

4. 标签

语句标签是可选的。只有如下语句需要被标签标识: while,do,for,switch。

return 语句

具有值的 return 语句不应该使用“()”(圆括号)包围值。返回值表达式必须和 return 关键字在同一行从而避免插入分号。

if 语句

if 语句应该使用如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# single
if (condition) {
statements;
}
#
if (condition) {
statements;
} else {
statements;
}
#
if (condition) {
statements;
} else if (condition) {
statements;
} else {
statements;
}

for 语句

for 语句应该使用如下格式:

1
2
3
4
5
6
7
8
# 和数组使用
for (initialization; condition; update) {
statements;
}
# 和对象使用
for (variable in object) {
statements;
}

第一种格式应该和数组使用。
第二种格式应该和对象使用。注意添加到对象的 prototype 中的成员将被包含在遍历中。通过使用 hasOwnProperty 方法来区分对象的成员是明智的:

1
2
3
4
5
for (variable in object) {
if (object.hasOwnProperty()) {
statements;
}
}

while 语句

while 语句应该使用如下格式:

1
2
3
while (condition) {
statements;
}

do 语句

do 语句应该使用如下格式:

1
2
3
do {
statements;
} while (condition);

不像其它复合语句,do 语句始终使用“;”(分号)结尾。

switch 语句

switch 语句应该有如下格式:

1
2
3
4
5
6
7
switch (expression) {
case expression:
statements;
break;
default:
statements;
}

每组语句(除了 default)应该以 break,return 或者 throw 结束。不要 fall through。

try 语句

try 语句应该使用如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
try {
statements;
} catch (variable) {
statements;
}
#
try {
statements;
} catch (variable) {
statements;
} finally {
statements;
}

continue some 语句

不要使用 continue some;语句。它会让方法的控制流程模糊。

with 语句

不要使用 with 语句。

this

仅在对象构造器, 方法, 闭包中使用.
this 的语义很特别. 有时它引用一个全局对象(大多数情况下), 调用者的作用域(使用 eval 时), DOM 树中的节点(添加事件处理函数时), 新创建的对象(使用一个构造器), 或者其他对象(如果函数被 call() 或 apply()).
使用时很容易出错, 所以只有在下面两个情况时才能使用:

  • 在构造器中
  • 对象的方法(包括创建的闭包)中
  • toString() 方法
    应该总是成功调用且不要抛异常.
    可自定义 toString() 方法, 但确保你的实现方法满足: (1) 总是成功 (2) 没有其他负面影响. 如果不满足这两个条件, 那么可能会导致严重的问题, 比如, 如果 toString() 调用了包含 assert 的函数, assert 输出导致失败的对象, 这在 toString() 也会被调用.

5. 函数

属性和方法

  • 文件或类中的 私有 属性, 变量和方法名应该以下划线 “_“ 开头.
  • 保护 属性, 变量和方法名不需要下划线开头, 和公共变量名一样.
    更多有关 私有 和 保护的信息见, visibility.

方法和函数参数

可选参数以 opt_ 开头.
函数的参数个数不固定时, 应该添加最后一个参数 var_args 为参数的个数. 你也可以不设置 var_args 而取代使用 arguments.
可选和可变参数应该在 [@param ] 标记中说明清楚. 虽然这两个规定对编译器没有任何影响, 但还是请尽量遵守

Getters 和 Setters

Getters 和 setters 并不是必要的. 但只要使用它们了, 就请将 getters 命名成 getFoo() 形式, 将 setters 命名成 setFoo(value) 形式. (对于布尔类型的 getters, 使用 isFoo() 也可.)

命名空间

  • JavaScript 不支持包和命名空间.
    不容易发现和调试全局命名的冲突, 多个系统集成时还可能因为命名冲突导致很严重的问题. 为了提高 JavaScript 代码复用率, 我们遵循下面的约定以避免冲突.
  • 为全局代码使用命名空间
    在全局作用域上, 使用一个唯一的, 与工程/库相关的名字作为前缀标识. 比如, 你的工程是 “Project ixdcw”, 那么命名空间前缀可取为 ixdcw.*.
1
2
3
4
var ixdcw = {};
ixdcw.sleep = function() {
...
};

明确命名空间所有权
当选择了一个子命名空间, 请确保父命名空间的负责人知道你在用哪个子命名空间, 比如说, 你为工程 ‘sloths’ 创建一个 ‘hats’ 子命名空间, 那确保 Sloth 团队人员知道你在使用 sloth.hats. 外部代码和内部代码使用不同的命名空间
“外部代码” 是指来自于你代码体系的外部, 可以独立编译. 内外部命名应该严格保持独立. 如果你使用了外部库, 他的所有对象都在 foo.hats.* 下, 那么你自己的代码不能在 foo.hats.*下命名, 因为很有可能其他团队也在其中命名.

6. 额外的建议

{}和[]

使用{}替代 new Object()。使用[]替代 new Array()。
当成员名字为连续的整数时使用数组。当成员名字为任意的字符串或名字时使用对象。
使用 Array 和 Object 语法, 而不使用 Array 和 Object 构造器.

1
2
3
4
5
6
7
8
9
10
11
var a  = [x1, x2, x3];
var a2 = [x1, x2];
var a3 = [x1];
var a4 = [];
var o = {};
var o2 = {
a: 0,
b: 1,
c: 2,
'strange key': 3
};

逗号操作符

不要使用逗号操作符,除了 for 语句的控制部分的严格使用。(这不适合逗号操作符,它应该用于对象字面量,数组字面量,var 语句和参数列表。

块作用域

在 JavaScript 里块没有作用域,只有方法有作用域。不要使用块,除了复合语句一定需要用到外。

赋值表达式

不要在 if 和 while 语句的条件部分做赋值。不要写不易懂的代码。

===和!==操作符

始终使用===和!==操作符会更好。和!=操作符会做类型强制转换。特别是,不要使用来和“假”值做比较。

令人混淆的加和减

注意不要在“+”后面跟“+”或“++”。这种模式令人混淆。在它们之间插入圆括号来让你的意图更清晰。

1
2
3
total = subtotal + +myInput.value;
// is better written as
total = subtotal + (+myInput.value);

这样“+ +”就不会被读错成“++”。

只用于解析序列化串 (如: 解析 RPC 响应)

eval 方法是 JavaScript 里最滥用的特性。不要使用它。
解析序列化串是指将字节流转换成内存中的数据结构. 比如, 你可能会将一个对象输出成文件形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
users = [
{
name: 'Eric',
id: 37824,
email: 'jellyvore@myway.com'
},
{
name: 'xtof',
id: 31337,
email: 'b4d455h4x0r@google.com'
},
...
];

很简单地调用 eval 后, 把表示成文件的数据读取回内存中.
类似的, eval() 对 RPC 响应值进行解码. 例如, 你在使用 XMLHttpRequest 发出一个 RPC 请求后, 通过 eval () 将服务端的响应文本转成 JavaScript 对象:

1
2
3
4
5
6
7
8
9
10
11
var userOnline = false;
var user = 'nusrat';
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('GET', 'http://chat.google.com/isUserOnline?user=' + user, false);
xmlhttp.send('');
// Server returns:
// userOnline = true;
if (xmlhttp.status == 200) {
eval(xmlhttp.responseText);
}
// userOnline is now true.

其他

不要使用 Function 构造函数。
不要传递字符串给 setTimeout 或者 setInterval。

JSDoc

  • 普通类型 {boolean}, {Window}, {goog.ui.Menu}

普通类型的描述方法.

  • 复杂类型

参数化类型, 即指定了该类型中包含的一系列”类型参数”. 类似于 Java 中的泛型.

  • 字符串数组{Array.<string>}
  • 键为字符串, 值为整数的对象类型 {Object.<string, number>}
  • 联合类型 {(number|boolean)}

一个整数或者布尔值. 表示其值可能是 A 类型, 也可能是 B 类型

  • 记录类型 {myNum: number, myObject}

由现有类型组成的类型. 表示包含指定成员及类型的值. 这个例子中, myNum 为 number 类型, myObject 为任意类型.
注意大括号为类型语法的一部分. 比如, Array.<{length}>, 表示一具有 length 属性的 Array 对象.

  • 可为空类型 {?number}

一个整型数或者为 NULL 表示一个值可能是 A 类型或者 null. 默认, 每个对象都是可为空的. 注意: 函数类型不可为空.

  • 非空类型 {!Object}

一个对象, 但绝不会是 null 值. 说明一个值是类型 A 且肯定不是 null. 默认情况下, 所有值类型 (boolean, number, string, 和 undefined) 不可为空.

  • 函数类型 {function(string, boolean)}

具有两个参数 ( string 和 boolean) 的函数类型, 返回值未知. 说明一个函数.

  • 函数返回类型 {function(): number}

函数返回一个整数. 说明函数的返回类型.

  • 函数的 this 类型 {function(this:goog.ui.Menu, string)}

函数只带一个参数 (string), 并且在上下文 goog.ui.Menu 中执行. 说明函数类型的上下文类型.

  • 可变参数 {function(string, ...[number]): number}

带一个参数 (字符类型) 的函数类型, 并且函数的参数个数可变, 但参数类型必须为 number. 说明函数的可变长参数.
可变长的参数 (使用 [@param ] 标记) [@param ] {…number} var_args
函数参数个数可变. 使用标记, 说明函数具有不定长参数.

  • 函数的 缺省参数 {function(?string=, number=)}

函数带一个可空且可选的字符串型参数, 一个可选整型参数. = 语法只针对 function 类型有效. 说明函数的可选参数.
函数 可选参数(使用 [@param ] 标记) [@param ] {number=} opt_argument
number 类型的可选参数. 使用标记, 说明函数具有可选参数.

  • 所有类型 {*}

表示变量可以是任何类型.

JS 类型

  • number

Number new Number(true) Number 对象

1
2
3
4
5
1
1.0
-5
1e5
Math.PI
  • string

字符串对象

1
2
3
4
5
'Hello'
"World"
String(42) # 字符串值
String new String('Hello')
new String(42)
  • boolean

布尔对象

1
2
3
4
5
true
false
Boolean(0) 布尔值
Boolean
new Boolean(true)
  • RegExp
1
2
new RegExp('hello')
/world/g
  • Date
1
2
new Date
new Date()
  • null
1
null
  • undefined
1
undefined
  • void
1
2
3
function f() {
return;
}
  • Array
1
2
3
4
5
6
7
['foo', 0.3, null]
# 类型不明确的数组
[]
# Array.<number>
[11, 22, 33] # 整型数组
# Array.<Array.<string>>
[['one', 'two', 'three'], ['foo', 'bar']] #字符串数组的数组
  • Object
    注意, JavaScript 中, 键总是被转换成字符串, 所以 obj[‘1’] == obj[1]. 也所以, 键在 for…in 循环中是字符串类型. 但在编译器中会明确根据键的类型来查找对象.
1
2
3
4
5
{}
{foo: 'abc', bar: 123, baz: null}
Object.<string> {'foo': 'bar'} 值为字符串的对象.
Object.<number, string> var obj = {};
obj[1] = 'bar'; 键为整数, 值为字符串的对象.
  • Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function(x, y) {
return x * y;
} 函数对象
function(number, number): number function(x, y) {
return x * y;
} 函数值
SomeClass /** @constructor */
function SomeClass() {}
#
new SomeClass();
SomeInterface /** @interface */
function SomeInterface() {}
#
SomeInterface.prototype.draw = function() {};
project.MyClass /** @constructor */
project.MyClass = function () {}
#
new project.MyClass()
project.MyEnum /** @enum {string} */
project.MyEnum = {
BLUE: '#0000dd',
RED: '#dd0000'
};
  • Element
    DOM 中的元素
1
document.createElement('div')
  • Node
    DOM 中的节点
1
document.body.firstChild
  • HTMLInputElement
    DOM 中, 特定类型的元素.
1
htmlDocument.getElementsByTagName('input')[0]

函数定义规范及说明

  • 内置函数采用下划线前缀命名
  • 函数传入的参数使用下划线命名
  • 内部定义的参数使用驼峰方式命名
  • jQuery 选择器调用元素的获取使用 $ 作为前缀
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 参数是下划线分隔
exports.countdown = function (btn_selector, str, time, end_str) {
// 按钮jquery 选择器使用 `$`
var $btn = $(btn_selector);
// 这里是驼峰命名
var countdownHandler = setInterval(_countdown, 1000);
// ...
function _countdown() {
var count_str = str.replace(/\{time\}/, count);
$btn.text(count_str);
if (count == 0) {
$btn.text(displayStr).removeAttr("disabled");
clearInterval(handlerCountdown);
}
count--;
}
// ...
};
  • 代码完成之后不得留有调试代码。
  • js 编写注意性能优化,如获取两次,调用两次可更正为获取一次,调用两次。
  • 插件中展示的内容尽量不用 table 标签( table 标签存在局限性),应用 ul 代替.
  • 功能函数前必须添加注释,注释函数功能。
  • 语句结束必须有‘;’。
  • if,else 执行语句用花括号‘{}’包裹起来。
  • 自编插件注意配置项的必选配置,需判断是否传入。

Web 编码规范 v3.0

本文规范合并 html/css 规范

文件及格式化

文件编码为 utf-8

更多 UTF-8 内容: http://zh.wikipedia.org/zh/UTF-8

使用 空格 进行缩进

使用 4 空格缩进来表明嵌套层次

网页大小

“网页大小”定义为网页的所有文件大小的总和,包括 HTML 文件和所有的嵌入的对象。用户喜欢快的而不是新奇的站点。网页主体加载速度控制在 3S 以内

注释

注释适用部分参考 公共约定

Html 部分

全局定义

DOCTYPE

页面文档类型统一使用 HTML5 DOCTYPE, 为每个 HTML 页面添加标准模式(standard mode)的声明,确保在每个浏览器中拥有一致的展现

1
<!doctype html>

字符集设置

声明方法遵循 HTML5 的规范, Meta 文件使用 “UTF-8” 浏览器显示编码指定, 通过声明一个明确的字符编码,让浏览器轻松、快速的确定网页内容渲染方式,通常指定为’UTF-8’

1
2
3
4
5
6
<html>
<head>
<meta charset="UTF-8" />
</head>
...
</html>

语言属性

为每个 HTML 页面根元素添加 lang 属性

根据 HTML5 规范:

强烈建议为 html 根元素指定 lang 属性,从而为文档设置正确的语言。这将有助于语音合成工具确定其所应该采用的发音,有助于翻译工具确定其翻译时所应遵守的规则等等。

1
2
3
<html lang="zh-CN">
<!-- ... -->
</html>

引入 CSS 和 JavaScript 文件

根据 HTML5 规范,在引入 CSS 和 JavaScript 文件时不需要指定 type 属性,因为 text/csstext/javascript 分别是它们的默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 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>

语法

特殊符号使用 HTML 字符实体(实体名称对大小写敏感)

常用如下:

符号 实体编码
空格 &nbsp;
© &copy;
¥ &yen;
® &reg;
> &gt;
< &lt;
& &amp;

td / th 要在 tr 里面,li 要在 ul / ol 里面

1
2
3
4
5
6
7
8
9
10
11
<!-- not good -->
<table>
<td>test</td>
</table>

<!-- good -->
<table>
<tr>
<td>test</td>
</tr>
</table>

ul / ol 的直接子元素只能是 li,不能包含其他元素

1
2
3
4
5
6
<!-- not good -->
<ul>
<span>123</span>
<li>a</li>
<li>b</li>
</ul>

行内元素里面不可使用块级元素

a 标签是一个行内元素,行内元素里面套了一个 div 的标签,这样可能会导致 a 标签无法正常点击

1
2
3
4
<!-- not good -->
<a href="../test">
<div></div>
</a>

可以使用如下代码进行修复:

1
2
3
<a href="../test" style="display: block">
<div></div>
</a>

不使用重复属性,重复的属性只会取第一个

1
2
3
4
5
<!-- error -->
<input class="a" type="text" class="b" />

<!-- good -->
<input class="a b" type="text" />

不要在 https 的链接里写 http 的图片

只要 https 的网页请求了一张 http 的图片,就会导致浏览器地址栏左边的小锁没有了,一般不要写死,写成根据当前域名的协议去加载,用//开头:

1
<img src="//static.chimeroi.com/hello-world.jpg" />

不要在自闭合(self-closing)元素的尾部添加斜线

HTML5 规范中说明这是可选的)

1
2
3
4
5
<!-- not good -->
<img src="logo.png" alt />

<!-- good -->
<img src="logo.png" alt />

不使用属性设置样式(img, table等元素)

1
2
3
4
5
<!-- not good -->
<img src="test.jpg" alt width="400" height="300" />

<!-- good -->
<img src="test.jpg" style="width:400px;height:300px;" />

自定义属性要以 data-开头

自己添加的非标准的属性要以 data-开头,否则w3c validator会认为是不规范的

1
2
3
4
5
<!-- not good -->
<div count="5"></div>

<!-- good -->
<div data-count="5"></div>

使用尽可能少的元素/嵌套

由于元素嵌套越多会是的浏览器解析速度出现问题, 所以规定元素嵌套不要超过六级, 尽量遵循 HTML 标准和语义,但是不要以牺牲实用性为代价;任何时候都要尽量使用最少的标签并保持最小的复杂度。

尽量避免多余的层级

1
2
3
4
5
6
7
<!-- not good -->
<span class="avatar">
<img src="..." />
</span>

<!-- good -->
<img class="avatar" src="..." />

兼容性

代码使用 html5 标准代码编写文档, 并且 ie8+, firefox, chrome 做到兼容, 禁止使用不兼容的标签,附录中包含了 html5 不支持的代码和新增的代码,这些标签禁止使用.
禁止使用特殊的标签

协议

如果链接和当前页面一致则忽略链接的协议部分, 建议在指向图片或其他媒体文件、样式表和脚本的 URL 地址中省略 http:, https:协议部分

1
<script src="//www.taobao.com/fp.js">

语义

使用符合语义的标签书写 HTML 文档, 选择恰当的元素表达所需的含义;

1
2
3
<a href="recommendations/">
All recommendations
</a>

大小写

元素的标签和属性名必须小写, 属性值必须加双引号;

1
<a href="/">Home</a>

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

  1. class
  2. id
  3. name
  4. data-*
  5. src, for, type, href, value , max-length, max, min, pattern
  6. placeholder, title, alt
  7. aria-*, role
  8. required, readonly, disabled

空格

去除不必要的空格

1
2
3
4
# Bad
<p>test </p>
# Good
<p>test</p>

嵌套和闭合

元素嵌套遵循 (X)HTML Strict 嵌套规则, 推荐使用 Firefox 插件 HTML Validator 进行检查;
正确区分自闭合元素和非自闭合元素.
非法闭合包括:<br>..</br>、<script />、<iframe />, 非法闭合会导致页面嵌套错误问题
自闭和标签: 以下元素不要求闭合, 原因见: HTML(5) 不要求标签自闭合
非闭合标签:area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr

引号

使用双引号来标识 html 的属性

1
2
3
4
5
6
7
8
9
# Bad
<a class='maia-button maia-button-secondary'>
Sign in
</a>

# Good
<a class="maia-button maia-button-secondary">
Sign in
</a>

自定义 javascript 属性

通过给元素设置自定义属性来存放与 JavaScript 交互的数据, 属性名格式为 data-xx (例如:data-lazyload-url)

1
<div class="bg bg-4"  data-load="false"></div>

目的是使用 js 调用时候对元素进行识别使用.

TODO

使用 TODO 来标记待做事情,便于后期搜索.
在 TODO 后添加 (姓名或邮件) 来表示分类

1
2
<!-- TODO(Mark Zhao): remove duplicate tag -->
<p><span>2</span></p>

焦点分离

将表现,行为和结构分离:不要在 html 和模板中加入除了结构以外的东西.例如内联样式, center 等标记.
在文档中引入尽可能少的样式和脚本

1
2
3
4
5
6
7
8
9
10
# Bad
<h1 style="font-size: 1em;">HTML sucks</h1>
<p>I've read about this on a few sites but now I'm sure:<u>HTML is stupid!!1</u><center>I can't believe there's no way to control the styling of my website without doing everything all over again!</center><p>

# Good
<h1>My first CSS-only redesign</h1>
<p>I've read about this on a few sites but today I'm actually
doing it: separating concerns and avoiding anything in the HTML of
my website that is presentational.
<p>

block,list 或 table 元素

针对每个 block,list 或 table 元素另起一行,并在每个子元素前缩进。这样可读性好

1
2
3
4
5
6
<ul>
<li>some list file</li>
...
</ul>
# ~
<table></table>

Table 的写法

  • </td> 结束标记应该与 <td> 处于同一行,不要换行, 如果换行,浏览器将会解析内容为内容+半角空格.
  • 不允许任何没有内容的空单元格存在,空单元格中必须存在
  • 表格高度和宽度的控制, 不出现多于一个的控制同一个单元格大小的 height 和 width, 保证任何一个 width 和 height 都是有效的,也就是你改动代码中任何一个 width 和 height 的数值,都应该在浏览器中看到变化
  • 一般情况下只有一列的表格,width 写在 <table> 的标签内
  • 只有一行的表格,height 写在 <table> 的标签内
  • 多行多列的表格,width 和 height 写在第一行或者第一列的 <td> 标签内
  • 尽量避免 colspan, rowspan 两个属性

实体字符

html 标签<, >、空格、特殊符号需要使用 html 实体

SEO 优化

  • a 元素必须加 title=""
  • img元素必须加 alt = ""
  • [ie7] button 参数必须带有 type=”submit” , 否则表单不会提交

注释

建议对超过一屏的代码页面模块进行注释, 以降低开发人员的嵌套成本和后期的维护成本.

1
2
3
4
5
<div id="sample">
<div class="someCategory">
...
</div><!-- .someCategory End -->
</div><!-- #sample End -->

Js 协作

Js 调用的 html 数据

使用 data- 作为前缀

事件驱动

使用事件驱动 js 事件, 不需要写 onclick="function" 而是采用 require 方式调用

id

html 中的 id 仅仅作为js句柄调用, 仅仅用于调用事件驱动, 不作为样式定义使用

Css 部分

此部分的约定包含 less, sass 等预编译的约定

项目文件规范

  • 左大括号放置在类的末尾
  • 修改的重要的注释使用以下来进行注释, 其余采用简洁注释

命名

文件命名

  • 文件使用小写/中杠线命名
  • 文件根据模块的名称来进行命名
  • 禁止使用拼音

类命名/模块嵌套

  • 禁止使用拼音
    命名格式是为了规范网站中样式命名混乱的问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 大区域命名
{$project}--{$component} {
// 小区域命名
{$component}-{$block}
}

$project(项目), $component(部件), $block(区块)
样式均不能包含 '--'(双中杠线), '-'(单中杠线)
样式建议使用小写字母标识, 尽量不要包含数字
-- 作为大区块分割线
- 作为小区块分割线


例如 fe 作为整个前端项目的标识

fe--editor : 为编辑器的命名
editor-top : 编辑器顶部命名
editor-footer : 编辑器底部命名
这里的
$project : fe
$component : editor
$block : [top, footer, ...]

html 代码

1
2
3
4
5
<div class="fe--editor">
<div class="editor-top"></div>
...
<div class="editor-footer"></div>
</div>

资源命名

文件夹中的资源图片名称和 文件 名称对应起来
例如是个人资料文件是 user.less, 这个资源应该放到指定资源目录的 user 文件夹中.

1
2
3
4
┝ user.less
┕ user # 文件夹
┝ ...
┕ header.png

less 中的变量、函数、mixin 等采用驼峰式命名

对于变量的命名采用(定义/模块) 来进行命名, 使用中尽量不要直接调用颜色, 而是调用模块的定义

1
2
3
4
5
6
7
8
9
@mainFontColor: #444;

:root {
--mod-border-color: @mainFontColor;
}

.mod {
color: var(--mode-border-color);
}

常用的标示符

1
2
3
4
5
# 大小
-xs # extreme small
-sm # small
-lg # large
-xl # extreme large

语法

所有声明语句都应当以分号结尾

最后一条声明语句后面的分号是可选的,但是,如果省略这个分号,你的代码可能更易出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* error */
.selector {
font-size: 15px
color: red
}

/* not good */
.selector {
font-size: 15px;
color: red
}

/* good */
.selector {
font-size: 15px;
color: red;
}

为选择器分组时,将单独的选择器单独放在一行

1
2
3
4
5
6
/* good */
.selector,
.selector-secondary,
.selector[type="text"] {
/* ... */
}

避免为 0 值指定单位

例如,用 margin: 0; 代替 margin: 0px;

  1. 为选择器中的属性添加双引号,例如,input[type="text"]
    某些情况下是可选的,但是,为了代码的一致性,建议都加上双引号

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* not good */
    .selector[type="text"] {
    /* ... */
    }

    /* good */
    .selector[type="text"] {
    /* ... */
    }
  2. 十六进制值应该全部小写,例如,#f3f6fa

  3. 不出现空的规则(声明块中没有声明语句)

  4. 不要设置太大的 z-index(一个正常的系统的层级关系在 10 以内就能完成)

  5. 多写注释,且多使用句子进行描述而不是词语

    1
    2
    3
    4
    5
    /* 为了去除输入框和表单点击时的灰色背景 */
    input,
    form {
    -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
    }
  6. 不要使用*选择器

  7. 适当使用:before:after来画页面的一些视觉上的辅助性元素,如三角形、短的分隔线、短竖线等,可以减少页面上没有用的标签

  8. 选择器不要超过 4 层(在 Less 中避免嵌套超过 4 层)

  9. border: 0; 代替 border: none;

  10. 使用简写形式的十六进制值,例如,用 #fff 代替 #ffffff

  11. 对于属性值或颜色参数,省略小于 1 的小数前面的 0 (例如,.5 代替 0.5-.5px 代替 -0.5px

写法技巧

样式定义可以覆盖公共样式

1
2
3
4
# a 是公共样式, 这里写的样式会覆盖掉公共样式
.my--profile a{
...;
}

元素嵌套级别不超过 6 级

  • js 调用的样式 J_xxxx 字母 J 加下滑线 _ ,并且不要将这些 class 包含到 CSS 文件中。

id 不能写 css 样式

由于 id 属性作为仅仅作为 js 来调用, 所以使用到 JS 调用 class 的时候使用 J_ 作为调用的句柄

1
2
3
4
5
<script>
$('.J_tabs').hover(function(){
...;
})
</script>

logo 最好和 h1 标题使用一个

logo 用背景图标来显示, 必须用“h1”来标识。 .head h1{}

空行

成组的 css 规则使用空行来分隔. 多分组使用多空行来分隔

love/hate 写法

a 标签采用 LOVEHATE 写法

l(link)ov(visited)e h(hover)a(active)te

1
2
3
4
a:link{}
a:visited{}
a:hover{}
a:active{}

混排时候的字体定义

中英文混排时,我们尽可能的将英文和数字定义为 verdana 和 arial 两种字体

这样为了避免字符使用宋体时候出现的各种问题
会将人民币符号(两横)表现成日元的符号(一横)等问题
宋体表现数字和字母不协调

字体字号

一般使用中文宋体 12px 和 14.7px 这是经过优化的字号,黑体字或者宋体字加粗时,一般选用 11pt 和 14.7px 的字号比较合适

尽量使用简写属性

这里 0 不需要单位, 对属性值为 0 的情况省略单位

1
2
3
.col-main .content{
padding: 0 1em 2em
}

元素的隐藏

  • display:none隐藏对象浏览器不作渲染,不占用内存。而visibility:hidden则会

对于隐藏元素使用 display:none

颜色使用 16 进制表示

颜色使用 16 进制的值表示

引号

尽可能不使用引号, 迫不得已使用单引号 '

css 精灵

合理使用 CSS Sprite, 并控制质量和文件大小

对图片大小进行优化
RIOT : 图片压缩工具

公共样式

宽度分组, 宽度用 w 前缀表示, 尽量不要定宽

从 w120 开始, 一直写到 12px - 720px 宽度使用 12px 作为步进

1
2
3
.w12{width:12px;}
...
.w720{width:720px;}

位置及监听

css 文件不需要自己去手动创建, 而是使用 compass, sass 来生成并且维护 css 样式

  1. 声明块的左花括号前添加一个空格

  2. 声明块的右花括号应当单独成行

  3. 每条声明语句的 : 后应该插入一个空格

  4. 每条样式声明应该独占一行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* not good */
    .selector {
    font-size: 15px;
    color: red;
    }

    /* good */
    .selector {
    font-size: 15px;
    color: red;
    }
  5. 对于以逗号分隔的属性值,每个逗号后面都应该插入一个空格(例如,box-shadowtransition

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* not good */
    .selector {
    transition: border 0.2s, color 0.3s, padding 0.4s;
    }

    /* good */
    .selector {
    transition: border 0.2s, color 0.3s, padding 0.4s;
    }
  6. !important前插入一个空格

  7. 注释://后插入一个空格,/*后插入一个空格,*/前插入一个空格

  8. Less 的操作符,在圆括号中的数学计算表达式的数值、变量和操作符之间均添加一个空格

  9. 注释统一用/* */( Less 中也不要用//

样式兼容性

  1. 当使用一些较新的 CSS3 语法时,应注意添加浏览器前缀( FAIS 2 打包工具包含 CSS 预处理,固无需考虑此条)

  2. 不要使用 input 的 line-height 来做垂直居中
    设置 line-height 为一个很高的值会导致 Safari 浏览器的输入光标变得巨大 (与 line-height 等高)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* not good */
    input {
    height: 40px;
    line-height: 40px;
    }

    /* good */
    input {
    height: 20px;
    line-height: 20px;
    padding: 10px 0;
    }

选择器权重(样式覆盖)

权重的基本规则:

  1. 相同的权重:以后面出现的选择器为最后规则
  2. 不同的权重,权重值高则生效

详细了解权重计算方法

  1. 非通用样式使用嵌套方式进行编写,避免影响其他自己不了解样式,造成样式覆盖
  2. Vue 中样式谨慎使用 scoped,会影响样式选择器性能,请使用第一点进行特有样式编写
  3. 样式需要修改时,尽量找到原样式声明进行修改
  4. 无法修改原样式声明时,应通过权重关系,编写权重更高的样式进行覆盖
  5. 不使用!important,除非原样式使用内联样式或!important且无法直接修改

声明简写

  1. 当你不确定自己写的属性会否影响到其他属性时,应避免使用简写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* error */
    .element {
    margin: 0 0 10px;
    background: red;
    background: url("image.jpg");
    border-radius: 3px 3px 0 0;
    }

    /* good */
    .element {
    margin-bottom: 10px;
    background-color: red;
    background-image: url("image.jpg");
    border-top-left-radius: 3px;
    border-top-right-radius: 3px;
    }
  2. 当你确定自己的声明不会影响到其他属性时,请使用简写提升代码简洁性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* not good */
    .element {
    padding-top: 10px;
    padding-right: 20px;
    padding-bottom: 15px;
    padding-left: 20px;
    }

    /* good */
    .element {
    padding: 10px 20px 15px;
    }

CSS 动画

  1. 不要使用 all 属性做动画

使用 transition 做动画的时候不要使用 all 所有属性,在有一些浏览器上面可能会有一些问题,如下:

1
transition: all 2s linear;

在 Safari 上面可能会有一些奇怪的抖动,正确的做法是要用哪个属性做动画就写哪个,如果有多个就用隔开,如下代码所示:

1
transition: transform 2s linear, opacity 2s linear;
  1. 位移动画使用 transform 替代 position (提升动画性能)
  2. 使用 CSS 动画替代 JS 动画

声明顺序

相关的属性声明按以下顺序做分组处理,组之间需要有一个空行

  1. Positioning(影响其他元素和自身位置相关声明)

  2. Box model(自身盒模型相关声明)

  3. Typographic(文本相关声明)

  4. Visual(自身样式)

  5. Misc(其他声明)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    .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;
    }

附录

v3.0(2021-11-12)

  • 合并 html / css 规范
  • 移除对 IE 的支持

v2.0(2017-08-26)

PHP 命名规范(Laravel 增补版)

代码风格统一

phpstorm 常用快捷键

1
2
3
4
ctrl/cmd + alt + L          # 格式化代码
ctrl/cmd + alt + O       # 项目 use 优化 / 优化导入
ctrl/cmd + shift + n       # 查找文件
ctrl/cmd + alt + shift + n # 查找函数

代码推送

master : 线上分支 develop : 开发分支

任何人不得直接在这两个分支进行开发, 开发完毕后将代码用 pr
的方式提交合并请求, 由 pr 负责人审核完成后才能够合并到 develop 分支或者
master 分支

单元测试

必须对接口写单元测试, 否则测试不通过的代码一律打回

参数传递

(一般情况下来讲)类函数少于或者等于 4 个参数的, 可以直接使用参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// before
public function beChid($data): bool
{
$account_id = data_get($data, 'account_id');
$initDb = [
'account_id' => $account_id,
'chid_status' => array_get($data, 'status'),
'chid_failed_reason' => array_get($data, 'reason') ?? '',
];
}

// after
public function beChid($account_id, $status, $reason = ''): bool
{
$initDb = [
'account_id' => $account_id,
'chid_status' => $status,
'chid_failed_reason' => $reason,
];
}

数据管理方式

  1. 本地数据库每日备份一次
  2. 线上数据库使用策略等方式, 每日备份一次
  3. 数据库采用独立服务器
  4. 软件服务器和数据库服务器分开请求, 不得走一个服务器,
    便于数据库独立分开
  5. 本地开发使用图片服务器进行图片的存储上传

数据模型

  • 注释
  • 报错
  • 数据库调用最小化

Vue 开发规范 1.0 (For Vue3)

本文是 JavaScript 编码规范Web 编码规范 的补充版, 涉及到 vue 独立性的内容在此单独说明

本规范旨在为前端程序的开发者提供规范化最新的指导,可用于程序员个人编译环境以及研发团队集成环境等场合的代码规范化检查。

本文约定于 vue3, 不适用于 vue2

环境约定

  1. Node.js LTS 版本,你可以使用 nvmnvm-windows 在一台电脑中管理多个 Node 版本

  2. 使用 Chrome 浏览器并安装 Vue.js devtools 进行调试

命名

  1. 文件名应该是大写驼峰, 文件名作为组件名称, 组件内不进行组件名称的定义

  2. 单文件组件的文件名应该要么始终是单词大写开头( PascalCase )

  3. 应用特定样式和约定的 基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// not good
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

// good
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue

components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue
  1. 和父组件 紧密耦合的子组件 应该以父组件名作为前缀命名

如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// not good
components/
|- TodoList.vue
|- TodoItem.vue
|- TodoButton.vue
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue

//good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue
  1. 组件名应该倾向于完整单词而不是缩写
1
2
3
4
5
6
7
8
9
// not good
components/
|- SdSettings.vue
|- UProfOpts.vue

// good
components/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

语法

  1. prop 的定义应该尽量详细,至少需要指定其类型

  2. v-for 设置键值;在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态

    在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态

  3. 不要把 v-if v-for 同时用在同一个元素上(大部分时候你可以使用计算属性实现)

  4. 模版中的组件名在单文件组件和字符串模板中组件名应该总是 PascalCase 的;

1
2
3
<!-- good -->
<!-- 在单文件组件和字符串模板中 -->
<MyComponent />
  1. JS/JSX 中的组件名应该始终是 PascalCase 的

  2. Prop 名大小写,在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 camelCase

1
2
3
4
5
// not good
props: {
'greetingText': String
}
<WelcomeMessage greetingText="hi"/>
  1. 组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法
1
2
3
4
5
6
7
8
9
10
11
// good
// 在模板中
{{ normalizedFullName }}
// 复杂表达式已经移入一个计算属性
computed: {
normalizedFullName: function () {
return this.fullName.split(' ').map(function (word) {
return word[0].toUpperCase() + word.slice(1)
}).join(' ')
}
}
  1. 非空 HTML 特性值应该始终带引号
1
2
3
<!-- good -->
<input type="text" />
<AppSidebar :style="{ width: sidebarWidth + 'px' }"></AppSidebar>
  1. 可简写指令需要缩写 (用 : 表示 v-bind: 和用 @ 表示 v-on:)

组件/实例的选项的顺序

组件/实例的选项应该有统一的顺序,这是我们推荐的组件选项默认顺序:

  1. 副作用 (触发组件外的影响)
    • el
  2. 全局感知 (要求组件以外的知识)
    • name
    • parent
  3. 组件类型 (更改组件的类型)
    • functional
  4. 模板修改器 (改变模板的编译方式)
    • delimiters
    • comments
  5. 模板依赖 (模板内使用的资源)
    • components
    • directives
    • filters
  6. 组合 (向选项里合并属性)
    • extends
    • mixins
  7. 接口 (组件的接口)
    • inheritAttrs
    • model
    • props/propsData
  8. 本地状态 (本地的响应式属性)
    • data
    • computed
  9. 事件 (通过响应式事件触发的回调)
    • watch
    • 生命周期钩子 (按照它们被调用的顺序)
      • beforeCreate
      • created
      • beforeMount
      • mounted
      • beforeUpdate
      • updated
      • activated
      • deactivated
      • beforeDestroy
      • destroyed
  10. 非响应式的属性 (不依赖响应系统的实例属性)
    • methods
  11. 渲染 (组件输出的声明式描述)
    • template/render
    • renderError

元素特性的顺序

元素 (包括组件) 的特性应该有统一的顺序,这是我们为元素特性推荐的默认顺序:

  1. 定义 (提供组件的选项)
    • is
  2. 列表渲染 (创建多个变化的相同元素)
    • v-for
  3. 条件渲染 (元素是否渲染/显示)
    • v-if
    • v-else-if
    • v-else
    • v-show
    • v-cloak
  4. 渲染方式 (改变元素的渲染方式)
    • v-pre
    • v-once
  5. 全局感知 (需要超越组件的知识)
    • id
  6. 唯一的特性 (需要唯一值的特性)
    • ref
    • key
    • slot
  7. 双向绑定 (把绑定和事件结合起来)
    • v-model
  8. 其它特性 (所有普通的绑定或未绑定的特性)
  9. 事件 (组件事件监听器)
    • v-on
  10. 内容 (覆写元素的内容)
    • v-html
    • v-text

单文件组件的顶级元素的顺序

单文件组件应该总是按照 <template><script><style> 的标签顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- good -->
<!-- ComponentA.vue -->
<template>...</template>
<style>
/* ... */
</style>

<!-- ComponentB.vue -->
<template>...</template>
<script>
/* ... */
</script>
<style>
/* ... */
</style>

隐性的父子组件通信

应该优先通过 prop 和事件进行父子组件之间的通信

一个理想的 Vue 应用是 prop 向下传递,事件向上传递的。遵循这一约定会让你的组件更易于理解。然而,在一些边界情况下 prop 的变更或 this.$parent 能够简化两个深度耦合的组件。

问题在于,这种做法在很多简单的场景下可能会更方便。但请当心,不要为了一时方便 (少写代码) 而牺牲数据流向的简洁性 (易于理解)。

非 Flux 的全局状态管理

应该优先通过 Vuex 管理全局状态

Vuex 提供的不仅是一个管理状态的中心区域,还是组织、追踪和调试状态变更的好工具。

iOS 命名规范(Object-c) - v1.1

目的

统一规范 Xcode 编辑环境下 Objective-C 的编码风格和标准。

适用范围

适应所有 Objective-C 语言开发的项目。

编码规范

文件

  • 项目文件使用有意义的单词,例如,工具类使用 Tools。
  • 公共文件统一命名为 Public.h。
  • 文件的目录按如下结构创建,例如图片等资源单独存放在 Images 文件夹下。

注释

单行注释采用//,多行注释采用/**/

排版格式

  • 代码的缩进应使用空格(SPACE),不能使用制表符(TAB),并且缩进以 2 个字符为单位。
  • 中括弧的每一个括弧在源程序中要单独占一行。
1
2
3
4
5
6
7
8
9
//不正确用法
for (int i = 0; i < 10 ; i++)
{
……
}
//正确用法
for (int i = 0; i < 10; i++){
……
}
  • 每行只能有一个语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//不正确写法
NSUInteger objectIndex, stuffCount;

objectIndex = objectIndex + 10, stuffCount = stuffCount + 20;

@synthesize MyView, MyLabelView;
//正确写法
NSUInteger objectIndex;
NSUInteger stuffCount;

objectIndex = objectIndex + 10;
stuffCount = stuffCount + 20;

@synthesize MyView;
@synthesize MyLabelView;

命名规范

保留字

Objective-C 语言的保留字或者关键词全部使用小写字母,例如,if,int 等。

方法

  • 方法的名称全部使用有意义的单词组成,且以小写开头,多单词组合时,后面的单词首字母大写,例如,-(void)getUserName。
1
2
3
4
5
6
7
8
9
10
// 方法的名称应全部使用有意义的单词组成,且以小写字母开头,多单词组合时,后面的单词首字母大写。
例如:
-(void)getUserInformation……
// 设置类变量的内容的方法应使用set作为前缀,读取变量的内容的方法应使用get作为前缀。
例如:
-(void)getUserName;
-(void)setUserName:(NSString *)userName;
// 方法参数命名
首字母小写,之后每个单词首字母都大写,具有足够的说明性,不需要添加类型前缀
例如:- (void)sendUserInfo:(NSDictionary *)userInfo

变量

变量必须起有意义的名字,第一个单词首字母小写,其他单词首字母大写,例如,Nsstring *usernameIphone;特殊类型的变量,命名时带上类型,例如 NsArray 的变量名 xxxArray;私有实例变量前加一个下划线,例如_myPrivate。

1
NSString  *username;
  • 对于一些特殊类型的变量,命名时要带上类型,如 NSArray 的变量命名为 xxxArray,其他的如 xxxDictionary,xxxSize 等。这样就可以从名称上知道是什么类型的变量。千万不能将 NSArray 的变量命名为 xxxDictionary。
  • 对于要和 interfacebuilder 关联的的输出口变量,命名时要后缀以特定的控件名。
1
IBOutlet UILabel *userNameLabel;
  • 对于使用 c 语言形式声明的变量,一些特定类型可采用一定的简写:
1
2
3
4
指针类型:P
结构体类型:Rec
数组类型:Arr
Core Graphic:CG
  • 循环控制变量通常使用单一的字符如:i、j、k 等。使用有意义的名字,如 objectIndex 也是可以的。
  • 尽量避免使用全局变量,如果必须使用全局变量则必须加前缀 ‘Pub_‘,同时应在变量名称中体现变量的类型。
  • 私有实例变量前加一个下划线,如_myPrivateVarible。

常量

  • 避免在程序中直接出现常数,使用超过一次的应以宏定义的形式来替代。
  • 常数的宏定义应与它实际使用时的类型相一致。如以 3.0 来定义浮点类型,用 3 表示整型。
  • 常量(预定义,局部常量等)使用小写 k 开头的驼峰法 例如:kInvalidHandle, kWritePerm
  • 一些常量前加特殊前缀,可以作为不同常量的区分,
1
2
3
UserDefaultsKey 的变量前加UDKEY_,
NotificationNameKey 前面加NNKEY_,
DictionaryKey 前面加DICTKEY_,

类命名 a) 首字母大写,之后每个单词首字母都大写 b)

使用能够反映类功能的名词短语 c) 文件和类同名

举例:BaseClient、ImageStore特殊类命名 a)

如果是视图控制器的子类应添加后缀”VC”或者”ViewController” b)

如果是视图的子类应添加后缀”View” c) 如果是按钮的子类应添加后缀”Button”

举例:SettingsVC、NavigationView d) 继承自 UIView 的类以 View 结尾。例如:

OperatorUsersInfomationView,LabelView 等。 e)

所有保存数据的实体以 Model 结尾。例如: UserModel分类(类别)命名

与类命名相同,此外需添加要扩展的类名和”+”举例:NSString+URLEncoding协议(委托)命名

与类命名相同,此外需添加”Delegate”后缀举例:ReplyViewDelegate

修改规范

注释

每个自定义函数进行说明,函数的参数可以说明,可以不说明;

全局的属性需要进行说明,常见性的可以不说明

  1. 多人协作完成项目时,public 接口的每个方法都应该添加关于函数,参数,返回值以及副作用的注释
  2. 当 if 语句的判断条件复杂时,需要用注释说明判断内容
  3. 接口类(继承于 BaseClient)的头文件每个方法前都应该注明方法的作用

其他规范

  1. 操作符前后都要加空格
  2. 避免相同的代码段在多个地方出现
  3. 语句嵌套层次不得超过 3 层
  4. 每个实现文件建议在 500 行以内,不能超过 1000 行,超过之后应考虑通过抽象类对代码进行重构
  5. 及时删除或注释掉无用的代码
  6. UITableViewCell 里面的 network client 都要委托出来
  7. 点击按钮之后需要切换按钮图片,当这两张图片没有关联时(例如一张图片相比另一张图片有选中效果),不应该设置为 UIControlSelected
  8. 控件布局使用相对坐标
  9. 确定不使用的代码应该删除

附加

  • 方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。
  • 函数分组和 protocol/delegate 实现中使用#pragma mark -来分类方法。
  • 使用属性时,实例变量应该使用 self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有 self.
  • 大括号在 case 语句中并不是必须的,除非编译器强制要求。当一个 case 语句包含多行代码时,大括号应该加上
  • 需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用 if 语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下
  • 单例对象应该使用线程安全模式来创建共享实例
1
2
3
4
5
6
7
8
9
10
+(DBManager *)sharedManager{
static DBManager *manager = nil;
static dispatch_once_t token;
dispatch_once(&token,^{
if(manager == nil) {
manager = [[DBManager alloc]init];
}
});
return manager;
}
  • 换行符是一个很重要的主题,因为它的风格指南主要为了打印和网上的可读性
  • dealloc 方法应该放在实现文件的最上面,并且刚好在 @synthesize @dynamic 语句的后面。在任何类中,init 都应该直接放在 dealloc 方法的下面。

init 方法的结构应该像这样:

1
2
3
4
5
6
7
- (instancetype)init {
self = [super init]; // 或者调用指定的初始化方法
if (self) {
// Custom initialization
}
return self;
}
  • 当访问一个 CGRect 的 x, y, width, height 时,应该使用 CGGeometry 函数代替直接访问结构体成员。苹果的 CGGeometry 参考中说到.
1
2
3
4
5
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
  • 私有属性应该声明在类实现文件的延展(匿名的类目)中.
1
2
3
4
5
@interface NYTAdvertisement ()
@property (nonatomic, strong) GADBannerView *googleAdView;
@property (nonatomic, strong) ADBannerView *iAdView;
@property (nonatomic, strong) UIWebView *adXWebView;
@end

项目约定

设计模式

项目开发使用 MVC 设计模式,如果后期项目开发控制器(ViewController)因业务逻辑导致代码过多,可以考虑其他的设计模式对代码进行拆分(如:MVVM 设计模式)

三方库的依赖管理

三方库的管理使用 CocoaPods 进行统一管理,如果使用其他的管理需要和其他开发人员进行说明使用流程及三方调用说明(如 Cartchage)

项目目录结构

项目分六个文件夹:Configs(配置模块)、Interfaces(界面模块)、Sources(资源模块)、Tools(工具模块)、Thirds(三方库)、main()

Configs:存放配置文件:pch 文件,宏定义文件,三方库的 AppKey 配置文件,swift 桥接文件,

Sources:存放项目需要的一些资源文件,里面包含多个文件夹,如:Images(图片资源)、Videos(视频资源)、Audios(音频资源)、Fonts(字体资源)

Thirds:存放一些导入的三方库,每个文件夹都问对应三方库的文件名称

Main:存放项目初始化时的几个文件:main 文件,info.plist 文件,Appdelegate 文件,Assets.xcasets 文件

Tools:为工具类,一些自定义工具、类别、网络请求类,URI 类,socket 管理类,支付管理类

Interfaces:为界面模块,所有的界面文件都在这个文件里面,该文件分为多个模块,这部分是最为复杂的,需要根据业务流程等因素去进行创建,创建时商讨一下

BaseVC(基类):该文件里面存放所有 ViewConroller,NavigationViewCotroller,webView 等类的基类,所有的控制器都继承该类

LoginRegister:登录注册模块,该文件夹问存放登录注册相关的界面

Protocols(协议模块):所有与协议有关的界面都放在这里,如登录注册时的登录注册协议

TarBars:该文件存放 tabbarViewController,tabbar(自定义 tabbar)等关于底部展示栏相关的类都放在这

Orders(订单模块):项目中所有的与订单有关的界面放在这,如车队订单,普通订单,订单详情,接单中心等所有与订单相关的界面,每种订单根据响应的业务在进行对应的划分:

Pays(支付模块):所有与支付相关的界面放在这

Appraiseals(评价模块):与评价相关的内容放在这里,如订单的评价,个人资料展示的评价内容等

Homes:首页模块

Messages(消息模块):该模块用于消息的展示,以及网易云 IM 相关的界面渲染类都放在这里

Mines(个人中心):该模块存放个人资料的展示,修改,及关于一些基本设置相关的东西等放在这里

Certifications(申请认证模块):一些需要进行审核相关东西放在这,如申请成为陪玩

老板和猎手分别对应的娱乐陪玩,上分教学,车队需要根据业务来进行具体的细分,目前对于业务了解不深,不好进行具体的分解,需要商讨

代码编写

文件的命名:

中间根据相关的业务进行具体的编写,结尾根据文件属性来写,如控制器 XXXViewController(LXDJXXXXVC),模型:XXXModel,

cell:XXXTVCell 或 XXXCVCell, 视图:XXXView,网络请求:XXXRequest,

工具类:XXXTool, 协议:XXXProtocol ,单利或者管理类:XXXManager,

方法命名: 已 xxx 结尾,如事件 Event:Listener, 动作

Action,请求已 Request 结尾(请求的方法名称可以根据后台提供的 URI 进行),UI 的创建或初始化已 UI 或 Config 结尾,普通方法已 Method 结尾

属性命名: _ 所有的属性都已改属性的类型结尾 _

数据类型属性需要对其进行初始化   代码位置: *

生命周期函数,多个是按照函数执行顺序依次实现 _ UI 创建或绘制函数 _

协议代理 _ 自定义函数或事件函数 _ 网络请求函数 * 懒加载

备注

  • 开发过程中禁止使用 storyboard
  • 开发中属性尽可能的使用 @property 声明,如果放在实现类的{}里面,需要在其前面加上下划线(_xxx)
  • 数组使用下标取值的时候,要进行判断,防止数组越界导致 App 闪退
  • 字典使用 key 取值的时候要判断 key 是否存在

附录

  • v1.1 2019-04-16 增加项目约定
  • v1.2 2017-03-01 第一版本的编码规范.

Android - 编码规范 v1.1

1. Android 命名规范

1.1 文件名

  • 文件名必须与类名或接口名一致。
  • 源代码文件名不能使用中文日文等英文数字之外的字符。
  • 源代码是识别大小写字母的。要求文件名无论在 WINDOWS 还是在 LINUX 都要用大小写区分。

1.2 项目命名规范

使用帕斯卡命名法命名。例:AndroidProject

1.3 包命名规范

命名规则:一个唯一包名的前缀总是全部小写的 ASCII 字母并且是一个顶级域名,通常是 com,edu,gov,mil,net,org。包名的后续部分根据不同机构各自内部的命名规范而不尽相同。这类命名规范可能以特定目录名的组成来区分部门 (department) ,项目(project),机器(machine),或注册名(login names)。

1
2
# 例
com.android.project

1.4 类、接口命名规范

命名规则:类名是个一名词,采用大小写混合的方式,每个单词的首字母大写。尽量使你的类名简洁而富于描述。使用完整单词,避免缩写词(除非该缩写词被更广泛使用,像 URL,HTML)

接口一般要使用 able、ible、er 等后缀

一般使用帕斯卡命名法,专有的缩写词除外。例:MainActivityRAMCache

1.5 方法命名规范

  • 使用驼峰命名法。
  • 不使用下划线分隔单词。
  • 方法命名应能描绘出方法的作用和功能。
  • 使用祈使动词或动词短语。
1
2
# 例
public void onCreate();

1.6 属性命名规范

1.6.1     变量命名

命名规则:第一个单词的首字母小写,其后单词的首字母大写。变量名不应以下划线或美元符号开头。变量名应简短且富于描述。变量名的选用应该易于记忆,即,能够指出其用途。尽量避免单个字符的变量名,除非是一次性的临时变量。临时变量通常被取名为 i,j,k,m 和 n,它们一般用于整型;c,d,e,它们一般用于字符型。

  • 一般使用下划线分隔(尽量统一用此种命名)
  • 其次使用驼峰命名法
  • 应为名词或名词短语
  • 简单易懂,不允许出现无意义的单词
1
2
3
# 例
private boolean is_start;
private String input_content;

1.6.2     常量命名

  • 建议都为大写字母
  • 用下划线分隔单词
1
private static final int MIN_VALUE = 0

1.7 XML 配置文件命名规范

1.7.1    Activity 布局文件命名规范

  • 一般使用下划线命名法。
  • 一般为 activity+下划线+对应的 Activity 名称

例:activity_main.xml

1.7.2    Fragment 布局文件命名规范

  • 一般使用下划线命名法。
  • 一般为 fragment+下划线+对应的 Fragment 名称

例:fragment_search.xml

1.7.3     列表控件中 Item 布局文件命名规范

  • 一般使用下划线命名法。
  • 一般为 item+下划线+所属环境名称+描述

例:item_ news_list.xml

1.7.4    Include 布局文件命名规范

  • 一般使用下划线命名法。
  • 一般为 include+所属环境名称+描述

例:include_ news_head_view.xml

1.8 XML 配置文件属性命名规范

控件 ID 命名

  • 一般使用下划线命名法。
  • 一般为:控件名称+下划线+控件描述。
1
2
3
4
5
6
# 例
@+id/tv_nickname
@+id/iv_avatar
@+id/ed_content
@+id/lv_search
@+id/rv_search

2 编码内容规范

2.1 文件内容

  • 每个源代码文件中最多只能定义一个 public 公共类。
  • 源代码文件的编码格式用 UTF-8。
  • 文件名要符合类名的命名规范。
  • 每个文件的源代码行数不能超过 800 行。

2.2 换行

  • 类和方法的头注释前要有两行空行。
  • 相邻两个代码块之前要有一行空行。
  • 变量声明与代码块之间要有一行空行。
  • 一条处理语句需要换行的时候,按照以下规则。
  • 在逗号后换行。
  • 运算符之前换行。
  • 运算优先级低的位置换行。
  • 换行前后的代码在同一缩进层次上。
  • 如果以上原则换行后影响到代码易读性的时候,可以缩进 8 个字符宽度。
  • 如果以上原则换行后其缩进格式与下一行代码相同时,要使用空行隔开。
  • 原则上“&&”和“||”之前使用换行。

2.3 空格

  • 关键字要在左括号“(”后留有一个空格,但 super 和 this 除外。
  • 关键字要在左括号“{”后留有一个空格。
  • 有括号“)”与左括号“{”之间要有一个空格。
  • “.”以外的二元运算符及 instanceof 的前后。
  • for 语句中分号“;”后面要有一个空格。
  • 逗号后留一空格。
  • =、+、-两边都各留一个空格。
  • “[”之后,“]”之前不留空格。

2.4 注释

2.4.1     共通

  • 在代码的开头处,如下所示描述头部注释。・块注释形式 ( /_ … _/ )・系统区分、业务区分・类描述 (类名、概要)・历史 (日期、V1.0 作为第一版,每次修改版本号就增加 0.1、创建者・更新者、更新内容)
  • 文档注释 Javadoc 的输出,参考指南。
  • 注释要清晰易懂。
  • 句子用 “ 。 “ 结束。
  • 块注释只能在注释掉一整块无效代码时使用。
  • 按照以下方式对修改过的代码标注释。・用 注释块” /_ … _/ “ 形式将需要修改的代码块注释掉,并添加修改人,修改前的版本号,修改后的版本号,修改内容概括。在修改后的代码块前后添加注释,标明修改人、修改后的版本号、修改内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
Modified by zhangyong 1.1->1.2 add update field
Java source …………
*/
// 2011/8/3 modified by zhangyu start
// 2011/8/3 modified by zhangyu end
// 注释一般采用" // "," // " 与注释内容间隔一个半角空格的距离。
// 行的后半段使用注释时,使用" // "并以相同的长度进行缩进。
// 为使程序代码易读,用//添加关于处理内容的注释说明。
// 极短的注释可以与它们所要描述的代码位于同一行,但是应该有足够的空白来分开代码和注释。
if (a == 2) {
return true; // special case
} else {
return isPrime(a); // works only for old
}

2.4.2     类注释

  • 类的注释在类声明语句的前一行按照以下示例描述。・功能概要 (类名、类说明、备注)・版本号・创建者:描述所属和姓名 (全称)

2.4.3     方法注释

  • 在函数定义之前记述函数注释,在注释中使用 javadoc 指定的标签。・功能概要 (方法名、方法说明、备注)。・参数说明:描述参数的类型、参数的内容,多个参数时分多行描述。・返回值说明:描述返回值的类型,返回值内容。・异常说明:描述异常类型、异常发生的条件。(描述方法抛出的异常)

PHP - 编码规范 v1.6

一、命名规则

1.命名规则概要

  1. 使用含义丰富的名字
1
2
3
4
5
// good
if ($currentYear > 2009) ...

// bad
if($t > 2009) ...
  1. 在缩写中,只将首字母大写
1
2
3
4
5
// good
function getHttpHost()

// bad
function getHTTPHost()

2. 类命名

  1. 类应该以名词单数形式, 首字母大写, 大小写混排,方式命名
1
class SqlStatement {	...	}
  1. 表示一组事物的类应使用复数形式
1
class SqlStatements {	...	}
  1. 类型放置在结尾
    对一个已知类型的变量还有同类变量我们采用尾部后缀来识别不同的类.
1
2
3
class ConnectionError extends Error {
// ...
}
  1. 接口的默认实现类可以以 Default 开头
1
2
3
class DefaultSqlBuilder extends SqlBuilderContract {
//...
}

3. 接口命名

接口的名字应为以单词 Contract 作为后缀, 以表明这是约束,或者是放入到文件夹中, 使用包名 来进行约束

1
2
3
interface SqlEngineContract{}

interface SortableContract{}
1
2
3
4
5
6
<?php namespace Poppy\Core\Rbac\Contracts;

interface RbacPermissionContract
{
// ...
}

4. 变量/属性命名

  1. 属性名应以小写字母开头, 采用驼峰法则.
1
public $userAuth;
  1. 常量的名字必须全部为大写字母
    所有字母大写,单词之间用下划线分割
1
2
const SEARCH_GOOGLE = 1;
const SEARCH_YAHOO = 2;
  1. 命名一组对象时,应使用复数形式
1
public $books;
  1. 布尔型变量不应使用否定性名字
1
2
3
4
5
6
7
// good
public $isFound;
public $isEnough;

// bad
public $isNotFound;
public $isNotEnough;
  1. 在嵌套循环中,使用有意义丰富的名字来命名循环控制变量
1
2
3
4
5
6
7
8
9
10
11
12
13
// good
for($row = 0; $row < getRows();$row++) {
for($col= 0;$col < getCols(); $col++) {
// ...
}
}

// bad
for($i = 0; $i < getRows();$i++) {
for($j= 0;$j < getCols(); $j++){
// ...
}
}
  1. 传入的变量采用蛇形写法, 自定义函数变量采用蛇形写法
1
2
3
4
5
6
7
8
9
10
11
12
13
// 控制器变量
class UserController extends Controller{
function login(Request $request) {
// 这里是蛇形写法
$order_status = $request->input('order_status');
}
}

// 自定义函数变量
// 这里传入的变量采用蛇形写法
function route_url($route, $params, $option_params){
// ...
}
  1. 同名称的不同类型使用匈牙利命名法则来区分
1
2
$arrCatids = array(1,2,3,4,5,6);
$strCatids = '1,2,3,4,5,6';

5. 函数命名

禁止拼音命名法

  1. 类函数名称以小写字母开头, 采用驼峰法则
1
function getCurrentYear()
  1. 用动词命名函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 动词表:
lists/ establish / delete / destroy
begin / end
first / last
get / release
get / set
increment / decrement
put / get
lock / unlock
open / close
min / max
old / new
start / stop
next / previous
source / target
show / hide
send / receive
cut / paste
up / down

// 系词表:
is / has
1
2
function startDatabase()
function getDatabaseStatus()
  1. 函数名字可以忽略类或对象名称,以避免重复
1
2
3
4
5
6
7
8
9
// good
class Font {
function getFamily();
}

// bad
class Font {
function getFontFamily();
}
  1. 单例类应该通过一个名为 getInstance()的静态函数返回他们唯一的值
1
2
3
4
5
6
class Toolkit {
private static const toolkit = new Toolkit();
public static function getInstance(){
return toolkit;
}
}

二、 文件格式/ 技巧

1. 缩进

代码使用 空格 缩进, 缩进采用 4 空格长度

2. 留白

恰当的使用空格可以有效提高代码的可读性

  1. 使用空格的通用规则

  2. 操作符 :(冒号), +(加号) 的前后都应有一个空格.

  3. 操作符 ,(逗号) 之后须有一个空格

1
2
3
4
5
6
7
8
9
10
11
// good
$bit = $bitStart + $bitEnd;
case 'someStr' :
mysqlConnection($config, $dbname);
if($count > 9)

// bad
$bit=$bitStart+$bitEnd;
case 'someStr':
mysqlConnection($config,$dbname);
if($count>9)
  1. 逻辑单元应该以空行分割
1
2
3
4
5
6
7
8
9
function drawCapture() {
$chars = getChars(5);

// imageCreate
$img = imageCreate();

// output image
outputImage();
}

3. 控制流程

  1. for,while,if 语句格式如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// for
for (init; condition; update) {
// ...
}

// while
while (condition) {
// ...
}

// if
if (condition) {
// ...
}
elseif (condition) {
// ...
}
else {
// ...
}
  1. 循环/条件语句必须以 { , }嵌套
1
2
3
4
5
6
7
8
9
10
11
// good
if ($i > 0) {
$val ++;
}
for ($i = 0; $i < $size; $i++) {
$val ++;
}

// bad
for($i=0; $i<$size; $i++) $val ++;
if($i > 0) $val ++;
  1. 使用临时变量以避免复合条件语句
1
2
3
4
5
6
7
8
9
10
// good
$itemValid = $itemMoney > 800 && $level > 3 && $valid > 0;
if($itemValid && isReady()) {
display();
}

// bad
if($itemMoney > 800 && $level > 3 && $valid > 0 && isReady()) {
display();
}
  1. Switches 语句应该套用以下格式,并且每个分支必须注释清楚
1
2
3
4
5
6
7
switch (condition) {
case 0 :
// show something
break;
default :
// this is some code
}

4. 声明

  1. 类/接口声明顺序
    类文档中的语句的顺序.
1
2
3
4
5
6
7
1.	文档/注释
2. 类/接口语句
3. 常量
4. 静态变量顺序:[public, protected, private]
5. 实例变量顺序:[public, protected, private]
6. 构造函数 __construct();
7. 函数 [public, protected, private] function();
  1. 变量的声明要在代码块的开头,而不是在用到它们的地方
1
2
3
4
5
6
7
public function method() {
$value = 0;
...
for (...) {
$value += $num;
}
}
  1. 变量需要先声明再使用

变量在使用前必须要初始化, 如果不初始化则会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// bad
foreach ($items as $item) {
$cash_over[] = $item->cash_over;
}

return view('desktop.finance_real.index', [
'cash_over' => $cash_over,
]);

// good
$cash_over = [];
foreach ($items as $item) {
$cash_over[] = $item->cash_over;
}

return view('desktop.finance_real.index', [
'cash_over' => $cash_over,
]);

5. 技巧

  1. 删除文件尾部的 ?>

php 文件的典型标记是以 <?php 开头, ?> 结尾。但是在 Zend Framework 中却不推荐在 php 文件末尾加 ?>
因为在<?php ?>之外的任何字符都会被输出到网页上,而之中的却不会。所以在末尾不加 ?> 可以预防 php 文件被恶意加入字符输出到网页。

  1. 数组的键名

在 PHP 中, 使用不经单引号包含的字符串作为数组键名是合法的, 但是我们不希望如此 – 键名应该总是由单引号包含而避免引起混淆. 注意这是使用一个字符串, 而不是使用变量做键名的情况

1
2
3
4
5
6
7
8
// 错误
$foo = $assoc_array[blah];
// 正确
$foo = $assoc_array['blah'];
// 错误
$foo = $assoc_array["$var"];
// 正确
$foo = $assoc_array[$var];
  1. 不要使用未初始化的变量
1
2
3
4
5
6
// 错误
if ($forum) ...
// 正确
if (isset($forum)) ...
// 正确
if (isset($forum) && $forum == 5)
  1. 避免在大数组上使用 in_array

避免在大的数组上使用 in_array(), 同时避免在循环中对包含 200 个以上元素的数组使用这个函数. in_array() 会非常消耗资源. 对于小的数组这种影响可能很小, 但是在一个循环中检查大数组可能会需要好几秒钟的时间. 如果您确实需要这个功能, 请使用 isset()来查找数组元素. 实际上是使用键名来查询键值. 调用 isset($array[$var]) 会比 in_array($var, array_keys($array)) 要快得多.

  1. SQL 脚本格式

SQL 代码常常会变得很长, 如果不作一定的格式规范, 将很难读懂. SQL 代码一般按照以下的格式书写, 以关键字换行:

1
2
3
4
5
6
$sql = 'SELECT *
<-one tab->FROM ' . SOME_TABLE . '
<-one tab->WHERE a = 1
<-two tabs->AND (b = 2
<-three tabs->OR b = 3)
<-one tab->ORDER BY b';

这里是应用了制表符后的例子:

1
2
3
4
5
6
$sql = 'SELECT *
FROM ' . SOME_TABLE . '
WHERE a = 1
AND (b = 2
OR b = 3)
ORDER BY b';
  1. 禁止使用单字母开头的变量(无意义的变量)
1
$tKey, $tVal

6. 空行的使用

  1. <?php [namespace] 之后必须有 1 个空行
1
2
3
4
5
<?php namespace System\Rbac\Helper;

use Illuminate\Database\Eloquent\Collection;

class RbacHelper{ /*...*/ }
  1. 两个函数之间至少有 1 个空行。
  2. return、die、exit 之前如果有其他语句的情况下应加上一个空行

三、 文档与注释

1. PHPDoc

PHPDoc 是一个 PHP 版的 Javadoc。它是一种注释 PHP 代码的正式标准。它支持通过类似 phpDocumentor 这样的外部文档生成器生成 API 文档,也可以帮助一些例如 Zend Studio, NetBeans, ActiveState Komodo Edit and IDE 和 Aptana Studio 之类的 集成开发环境 理解变量类型和弱类型语言中的其他歧义并提供改进的代码完成,类型提示和除错功能。
参考地址: http://zh.wikipedia.org/zh/PHPDoc

2. 注释

  1. 类注释
1
2
3
4
/**
* 这是某个类的介绍
*/
class SomeClass{}
  1. 注释局部变量
1
2
3
4
5
6
7
8
9
function someFunction(){
/** @var int $result 获取到的结果集 */
$result = 0;

/** array $searchResult 获取到的搜索结果集*/
var $searchResult;

// ...
}
  1. 注释类变量
1
2
3
4
5
/**
* The description of name
* @var string
*/
public $name
  1. 注释函数
    注释的标记应按照以下顺序
1
2
3
* 	@param
* @return
* @see

3. 文档注释

1
2
3
4
5
6
/**
 * 文件头部说明
 *
 * @author     Mark (zhaody901@126.com)
 * @copyright  Copyright (c) 2014-2016 sour-lemon team
 */

四、框架技巧

1.   控制器方法

1
2
3
4
5
lists()        # 列表
establish() # 创建 / 编辑
update() # 更新 / 批量更新
delete() # 删除
destroy() # 销毁(彻底删除)

2. 路由写法

路由器第二个参数不可以传 key

1
2
3
4
5
// 传值 bad
route('dsk_base_area.establish', ['parent_id' => $item['areaid']])

// 不传值 good
route('dsk_base_area.establish', [$item->areaid]])

这两个哪个写起来更简洁呢?
因为使用 route 的时候接收到的参数在控制器传参数进行获取
public function establish($parent_id) 这种方法才能够接收到数据的.
ps: 使用 $request->input('parent_id') 根本获取不到东西

3. 使用对象和对象的错误提示

1
2
3
4
5
6
7
// 使用对象的好处
route('dsk_base_area.create', [$item['areaid']]) # 如果不存在字段, 则报 undefined index 错误
route('dsk_base_area.create', [$item->area_id]) # 这里不报错的.

// 使用映射过的对象的好处是容易识记
route('dsk_base_area.create', [$item->areaid]) # 这里不报错的.
route('dsk_base_area.create', [$item->area_id]) # 使用映射过的字段更便于记忆, 减少浏览器的 `typo` 错误

4. 合理使用模型提供的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 取一条
UserMessage::where('item_id', $item_id)->select("*")->first();
UserMessage::where('item_id', $item_id)->first();
UserMessage::find($item_id);

// 取单个
UserMessage::where('item_id', $item_id)->lists('username')->first();
UserMessage::where('item_id', $item_id)->value('username');

// 模型方法
$item = UserMessage::find($item_id);
$item->num += 1;
$item->save();

5. Form 使用 post 方法提交可以不填写 ‘method’

1
2
3
4
5
6
// 这里来自于表单提交
@if (isset($item))
{!! Form::model($item,['route' => ['dsk_adv_item.edit', $item->id], 'id' => 'form_ad_place','method' => 'post']) !!}
@else
{!! Form::open(['route' => 'dsk_adv_item.create','id' => 'form_ad_place','enctype'=>'multipart/form-data']) !!}
@endif

优化后

1
2
3
4
5
@if (isset($item))
{!! Form::model($item,['route' => ['dsk_adv_item.edit', $item->id], 'id' => 'form_ad_place']) !!}
@else
{!! Form::open(['route' => 'dsk_adv_item.create','id' => 'form_ad_place']) !!}
@endif

6. 对于编辑/创建使用同一个模版

编辑和创建来说, 我们使用同一个模版, 模板的名字应该命名为 item.blade.php

7. [批量]更新使用 update

因为这里的更新就是批量的, 并且使用的方式不是更新一个, 所以这里不使用 batchUpdate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class AdPlaceController{
// bad
public function batchUpdate() {
// ...
}


// good
public function update() {
$update = \Input::input('update');
foreach ($update as $id => $item) {
AdvPlace::where('id', $id)->update($item);
}
return site_end('success', '更新成功');
}
}

附录 Appendices

附录 A:参考文档

附录 B:PHPDoc 标签参考

在线版地址 : http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.pkg.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@abstract		Documents an abstract class, class variable or method.
@access public, private or protected Documents access control for an element. @access private indicates that documentation of element be prevented.
@author author name <author@email> Documents the author of the current element.
@category Specify a category to organize the documented element’s package into
@copyright name date Documents copyright information.
@deprecated version Documents a method as deprecated.
@example /path/to/example Documents the location of an external saved example file.
@exception documents an exception thrown by a method — also see @throws.
@global type $globalvarname Documents a global variable or its use in a function or method.
@ignore Prevents the documentation of an element
@internal private information for advanced developers
@link URL
@name global variable name Specifies an alias for a variable. For example, $GLOBALS[‘myvariable’] becomes $myvariable
@magic phpDocumentor tags}-.
@package name of a package Documents a group of related classes and functions.
@param type [$varname] description
@return type description This tag should not be used for constructors or methods defined with a void return type.
@see Documents an association to another method or class.
@since version Documents when a method was added to a class.
@static Documents a static class or method
@staticvar Documents a static variable’s use in a function or class
@subpackage
@throws Documents an exception thrown by a method.
@todo Documents things that need to be done to the code at a later date.
@var type a data type for a class variable
@version Provides the version number of a class or method.

附录 C: 版本变化

v1.6(2020 年 12 月 21 日)
加入空格约定
约定接口写法
v1.5(2018 年 04 月 29 日)
变更格式
对 interface 约定做修改
增加 Laravel 技巧
v1.4(2016 年 10 月 07 日)
更改为 markdown 格式, 并且将其替换为 Laravel 编码格式
V1.3(2015 年 4 月 19 日)
项目文件结构说明
V1.2(2013 年 4 月 27 日)
分离项目公共部分
V1.1(2013 年 4 月 2 日)
增加左格式化内容
增加删除 ?> 标记
V1.0(2012 年 11 月 7 日)
初始化规范

React - 开发规范 v2.1

本文是 JavaScript 编码规范Web 编码规范 的补充版, 涉及到 react 独立性的内容在此单独说明

此风格指南主要基于目前流行的 JavaScript 标准。对于项目中是否应允许使用一些惯例(如 async/await,静态 class 属性等)的问题,应视具体情况而定。目前,本指南不包含并且不推荐使用任何第三阶段的功能。

译者注:第三阶段指 TC39 流程中的 Stage 3 - Candidate。每个新的 ECMAScript 提案从起草到完成共分五个阶段。

一. 命名规范

1. 基本规范

  • 每个文件只写一个模块.模块名称使用帕斯卡命名
  • 推荐使用 JSX 语法.
  • 不要使用 React.createElement,除非在从一个非 JSX 的文件中初始化 app.
  • 只有在使用 arrayOf, objectOf,   或 shape 明确指出 arraysobjects 所包含的内容时,react/forbid-prop-types   才会允许这个属性 (props).

2. 文件命名规范

  • 目录使用小写蛇形排列
  • 文件名为首字母大写的驼峰
1
src/main/Main.js

目录说明

1
2
3
4
5
6
7
8
9
10
11
src/        # 源文件
assets # 资源
img : 图片
less : less 文件
style : 生成的样式文件
xxxx : 功能目录
model : 核心JS代码
System.js : 系统定义, 包含路由, 请求地址
Util.js : util 函数
public/ # 公共目录
.env.local : 配置文件

变量常用前缀

1
2
3
ds         :  数据源(dataSource)
lang : 语言前缀(Language)
is/has : 是否存在

Url 传入的参数

url 传入参数采用蛇形写法

1
let room_id = this.props.match.room_id;

3. 缩进以及换行

  • 一行能够展示开的元素, 不要两行展示
  • 如需换行, 属性遵循 4 格缩进

二. React 使用规则

1. 创建模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// bad
const Listing = React.createClass({
// ...
render() {
return <div>{this.state.hello}</div>;
},
});

// good
class Listing extends React.Component {
// ...
render() {
return <div>{this.state.hello}</div>;
}
}

如果你的模块没有状态或是没有引用refs, 推荐使用普通函数(非箭头函数)而不是类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}

// bad (relying on function name inference is discouraged)
const Listing = ({ hello }) => <div>{hello}</div>;

// good
function Listing({ hello }) {
return <div>{hello}</div>;
}

2. 严禁使用 Mixins

为什么? Mixins 会增加隐式的依赖,导致命名冲突,并且会以雪球式增加复杂度。在大多数情况下 Mixins 可以被更好的方法替代,如:组件化,高阶组件,工具模块等。

3. 命名

  • 扩展名: React 模块使用 .js 扩展名.

    eslint: No .jsx extension?

  • 文件名: 文件名使用帕斯卡命名. 如, ReservationCard.js.

  • 引用命名: React 模块名使用帕斯卡命名,实例使用骆驼式命名. eslint: react/jsx-pascal-case

1
2
3
4
5
6
7
8
9
10
11
// bad
import reservationCard from "./ReservationCard";

// good
import ReservationCard from "./ReservationCard";

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;
  • 模块命名: 模块使用当前文件名一样的名称. 比如 ReservationCard.js 应该包含名为 ReservationCard的模块. 但是,如果整个文件夹是一个模块,使用 index.js作为入口文件,然后直接使用 index.js 或者文件夹名作为模块的名称:
1
2
3
4
5
6
7
8
// bad
import Footer from "./Footer/Footer";

// bad
import Footer from "./Footer/index";

// good
import Footer from "./Footer";
  • 高阶模块命名: 对于生成一个新的模块,其中的模块名 displayName 应该为高阶模块名和传入模块名的组合. 例如, 高阶模块 withFoo(), 当传入一个 Bar 模块的时候, 生成的模块名 displayName 应为 withFoo(Bar).

为什么?一个模块的 displayName 可能会在开发者工具或者错误信息中使用到,因此有一个能清楚的表达这层关系的值能帮助我们更好地理解模块发生了什么,更好地进行 Debug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// bad
export default function withFoo(WrappedComponent) {
return function WithFoo(props) {
return <WrappedComponent {...props} foo />;
}
}

// good
export default function withFoo(WrappedComponent) {
function WithFoo(props) {
return <WrappedComponent {...props} foo />;
}

const wrappedComponentName = WrappedComponent.displayName
|| WrappedComponent.name
|| 'Component';

WithFoo.displayName = `withFoo(${wrappedComponentName})`;
return WithFoo;
}
  • 属性命名: 避免使用 DOM 相关的属性来用作其他的用途。

为什么?对于styleclassName这样的属性名,我们都会默认它们代表一些特殊的含义,如元素的样式,CSS class 的名称。在你的应用中使用这些属性来表示其他的含义会使你的代码更难阅读,更难维护,并且可能会引起 bug。

1
2
3
4
5
// bad
<MyComponent style="fancy" />

// good
<MyComponent variant="fancy" />

4. 声明模块

  • 不要使用 displayName 来命名 React 模块,而是使用引用来命名模块, 如 class 名称.
1
2
3
4
5
6
7
8
9
// bad
export default React.createClass({
displayName: 'ReservationCard',
// stuff goes here
});

// good
export default class ReservationCard extends React.Component {
}

5. 代码对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// bad
<Foo superLongParam="bar"
anotherSuperLongParam="baz" />

// good, 有多行属性的话, 新建一行关闭标签
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
/>

// 若能在一行中显示, 直接写成一行
<Foo bar="bar" />

// 子元素按照常规方式缩进
<Foo
superLongParam="bar"
anotherSuperLongParam="baz"
>
<Quux />
</Foo>

// bad
{showButton &&
<Button />
}

// bad
{
showButton &&
<Button />
}

// good

{showButton && (
<Button />
)}

// good
{showButton && <Button />}

对于一般的元素显示使用的三元运算符首选以下格式化方式

1
2
3
{
carCreateCondition.members && carCreateCondition.members.length ? this.renderAny() : this.renderSomeThing();
}

6. 单引号还是双引号

  • 对于 JSX 属性值总是使用双引号("), 其他均使用单引号('). eslint: jsx-quotes

为什么? HTML 属性也是用双引号, 因此 JSX 的属性也遵循此约定.

1
2
3
4
5
6
7
8
9
10
11
// bad
<Foo bar='bar' />

// good
<Foo bar="bar" />

// bad
<Foo style={{ left: "20px" }} />

// good
<Foo style={{ left: '20px' }} />

7. 空格

1
2
3
4
5
6
7
8
9
10
11
12
// bad
<Foo/>

// very bad
<Foo />

// bad
<Foo
/>

// good
<Foo />
1
2
3
4
5
// bad
<Foo bar={ baz } />

// good
<Foo bar={baz} />

8. 属性

  • JSX 属性名使用骆驼式风格camelCase.
1
2
3
4
5
6
7
8
9
10
11
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>

// good
<Foo
userName="hello"
phoneNumber={12345678}
/>
1
2
3
4
5
6
7
8
9
10
11
12
// bad
<Foo
hidden={true}
/>

// good
<Foo
hidden
/>

// good
<Foo hidden />
  • <img> 标签总是添加 alt 属性. 如果图片以 presentation(感觉是以类似 PPT 方式显示?)方式显示,alt 可为空, 或者<img> 要包含role="presentation". eslint: jsx-a11y/alt-text
1
2
3
4
5
6
7
8
9
10
11
// bad
<img src="hello.jpg" />

// good
<img src="hello.jpg" alt="Me waving hello" />

// good
<img src="hello.jpg" alt="" />

// good
<img src="hello.jpg" role="presentation" />
  • 不要在 alt 值里使用如 “image”, “photo”, or “picture”包括图片含义这样的词, 中文也一样. eslint: jsx-a11y/img-redundant-alt

为什么? 屏幕助读器已经把 img 标签标注为图片了, 所以没有必要再在 alt 里说明了.

1
2
3
4
5
// bad
<img src="hello.jpg" alt="Picture of me waving hello" />

// good
<img src="hello.jpg" alt="Me waving hello" />
1
2
3
4
5
6
7
8
// bad - not an ARIA role
<div role="datepicker" />

// bad - abstract ARIA role
<div role="range" />

// good
<div role="button" />

为什么? 屏幕助读器在键盘快捷键与键盘命令时造成的不统一性会导致阅读性更加复杂.

1
2
3
4
5
// bad
<div accessKey="h" />

// good
<div />
  • 避免使用数组的 index 来作为 key 属性的值。

译者注:key 属性指名称为 key 的属性,即 props.key

应当使用稳定不变的 ID。(使用不稳定的 ID 是一个反面模式,会降低性能、造成组件状态出错) 。特别是当元素的顺序可能改变的情况下,不应使用数组的 index   作为 key.

译者注:反面模式 (Anti-Pattern),指低效或是有待优化的软件设计模式。

1
2
3
4
5
6
7
8
9
// bad
{
todos.map((todo, index) => <Todo {...todo} key={index} />);
}

// good
{
todos.map((todo) => <Todo {...todo} key={todo.id} />);
}
  • 对于所有非必须的属性,总是手动去定义defaultProps属性.

为什么? propTypes 可以作为模块的文档说明, 并且声明 defaultProps 的话意味着阅读代码的人不需要去假设一些默认值。更重要的是, 显示的声明默认属性可以让你的模块跳过属性类型的检查.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// bad
function SFC({ foo, bar, children }) {
return (
<div>
{foo}
{bar}
{children}
</div>
);
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};

// good
function SFC({ foo, bar, children }) {
return (
<div>
{foo}
{bar}
{children}
</div>
);
}
SFC.propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
children: PropTypes.node,
};
SFC.defaultProps = {
bar: "",
children: null,
};
  • 尽可能少地使用扩展运算符

为什么? 除非你很想传递一些不必要的属性。对于 React v15.6.1 和更早的版本,你可以给 DOM 传递一些无效的 HTML 属性

例外情况:

  • 使用了变量提升的高阶组件
1
2
3
4
5
6
7
8
9
10
11
12
function HOC(WrappedComponent) {
return class Proxy extends React.Component {
Proxy.propTypes = {
text: PropTypes.string,
isLoading: PropTypes.bool
};

render() {
return <WrappedComponent {...this.props} />
}
}
}
  • 只有在清楚明白扩展对象时才使用扩展运算符。这非常有用尤其是在使用 Mocha 测试组件的时候。
1
2
3
4
5
6
7
8
export default function Foo {
const props = {
text: '',
isPublished: false
}

return (<div {...props} />);
}

特别提醒:尽可能地筛选出不必要的属性。同时,使用prop-types-exact来预防问题出现。

1
2
3
4
5
6
7
8
9
10
11
// bad
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...this.props} />
}

// good
render() {
const { irrelevantProp, ...relevantProps } = this.props;
return <WrappedComponent {...relevantProps} />
}

9. Refs 引用

1
2
3
4
5
6
7
8
9
// bad
<Foo
ref="myRef"
/>

// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>

10. 括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// bad
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>;
}

// good
render() {
return (
<MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
);
}

// good, 单行可以不需要
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}

11. 标签

1
2
3
4
5
// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />
1
2
3
4
5
6
7
8
9
10
// bad
<Foo
bar="bar"
baz="baz" />

// good
<Foo
bar="bar"
baz="baz"
/>

12. 函数

  • 使用箭头函数来获取本地变量。这使得传递数据给事件处理器 (event handler) 很方便。不过,尤其是当传递给自定义的纯组件 (PureComponent) 时,要确保这些箭头函数对性能影响不是太大,因为它们会每次都会触发可能无意义的重新渲染。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ItemList(props) {
return (
<ul>
{props.items.map((item, index) => (
<Item
key={item.key}
onClick={(event) => {
doSomethingWith(event, item.name, index);
}}
/>
))}
</ul>
);
}
  • 当在 render() 里使用事件处理方法时,不要提前在构造函数里把 this 绑定上去. eslint: react/jsx-no-bind

在类成员变量 (class fields) 里不要使用箭头函数,因为箭头函数会造成它难以测试和调试,并会降低性能。从概念上讲,类成员变量存的应该是数据,而不是逻辑或方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// bad
class extends React.Component {
onClickDiv() {
// do stuff
}

render() {
return <div onClick={this.onClickDiv.bind(this)} />;
}
}

// very bad
class extends React.Component {
onClickDiv = () => {
// do stuff
}

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

// good
class extends React.Component {
constructor(props) {
super(props);

this.onClickDiv = this.onClickDiv.bind(this);
}

onClickDiv() {
// do stuff
}

render() {
return <div onClick={() => {this.onClickDiv()}} />;
}
}
  • 在 React 模块中,不要给所谓的私有函数添加 _ 前缀,本质上它并不是私有的.

为什么?_ 下划线前缀在某些语言中通常被用来表示私有变量或者函数。但是不像其他的一些语言,在 JS 中没有原生支持所谓的私有变量,所有的变量函数都是共有的。尽管你的意图是使它私有化,在之前加上下划线并不会使这些变量私有化,并且所有的属性(包括有下划线前缀及没有前缀的)都应该被视为是共有的。了解更多详情请查看 Issue #1024, 和 #490

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// bad
React.createClass({
_onClickSubmit() {
// do stuff
},

// other stuff
});

// good
class extends React.Component {
onClickSubmit() {
// do stuff
}

// other stuff
}
1
2
3
4
5
6
7
8
9
// bad
render() {
(<div />);
}

// good
render() {
return (<div />);
}

三. 模块生命周期函数

1. 顺序

  1. 可选的 static 方法
  2. constructor 构造函数
  3. getChildContext 获取子元素内容
  4. componentWillMount 模块渲染前
  5. componentDidMount 模块渲染后
  6. componentWillReceiveProps 模块将接受新的数据
  7. shouldComponentUpdate 判断模块需不需要重新渲染
  8. componentWillUpdate 上面的方法返回 true, 模块将重新渲染
  9. componentDidUpdate 模块渲染结束
  10. componentWillUnmount 模块将从 DOM 中清除, 做一些清理任务
  11. render render() 方法
  12. 点击回调或者事件处理器onClickSubmit()onChangeDescription()
  13. render 里的 getter 方法getSelectReason()getFooterContent()
  14. 可选的 render 方法renderNavigation()renderProfilePicture()

2. 如何定义 propTypes, defaultProps, contextTypes, 等等其他属性…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
```jsx
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
id: PropTypes.number.isRequired,
url: PropTypes.string.isRequired,
text: PropTypes.string,
};

const defaultProps = {
text: 'Hello World',
};

class Link extends React.Component {
static methodsAreOk() {
return true;
}

render() {
return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>;
}
}

Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


## 四. 项目约定


### 1. isMounted 不要使用


- 不要再使用 `isMounted`. eslint: [`react/no-is-mounted`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md)



> 为什么? [`isMounted` 反人类设计模式:()](https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html), 在 ES6 classes 中无法使用, 官方将在未来的版本里删除此方法.



### 2. 组件是否显示采用内部处理


不要在外部进行外部组件判定


```jsx
{/* Bad */}
{self.kickRoomPopupIsShow
?
<KickRoomPopup
accountId={self.paramAccountId}
closeWin={() => {
this.setState({kickRoomPopupIsShow : false
})
}/>
: ''
}

{/* Good */}
<KickRoomPopup
visible={self.kickRoomPopupIsShow}
accountId={self.paramAccountId}
onClose={() => {
this.setState({kickRoomPopupIsShow : false
})
}/>

3. 数据存储(Store)

  • reducers 里面的方法大写, 采用显式传值的方法
  • 更改 dva 数据状态, 见伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { queryData } from "../services";
import { message } from "antd";
import _ from "lodash-es";

const home = {
namespace: "home",
state: {
data: [],
},
effects: {
*quertData({ payload: params }, { put, call }) {
const { success, data, message: msg } = yield call(queryData, params);
if (success && data) {
yield put({
type: "SET_DATA",
payload: { data: data },
});
} else {
message.error(msg);
}
}
},
reducers: {
SET_DATA: (state, { payload }) => ({
...state,
data: payload.data,
})
},
subscriptions: {},
};
export default home;

4. 多环境打包

umi 多环境打包

详细见伙玩 PC package.json 配置

  • dev 环境

yarn start:dev
yarn build:dev

  • pre 环境

yarn start:pre
yarn build:pre

  • test 环境

yarn start:test
yarn build:test

  • prod 环境

yarn start:prod
yarn build:prod

备注 & 参考

版本更新

  • v2.1 (2020-12-23)
    增加 Store 的数据约定
  • v2.0 (2020-05-11)
    增加 Airbnb 规范指南, 进行完整的完善, 在除了基本文件目录的基础上增加了更为详尽的规范
  • v1.1 (2020-02-26)
    规范组件写法
  • v1.0 (2019-mid)
    初始化文档

参考