序
每个团队的成员都是团队的一部分,产出与维护总是相互交织的,你种的树可能以后他也会乘凉,他埋的雷可能有一天会被你踩,为了让我们都成为互帮互助的“种树人”,所以这些各种写法皆可的情况下,希望通过一个统一的规范,来指导大家,方便大家编码,也方便后续的阅读维护
1、变量命名
团队的编码命名风格是小驼峰
// 大部分项目的Eslint的驼峰检查都会开着
//"camelcase": ["error", { "properties": "always" }]
// Bad:
import { no_camelcased } from "external-module"
var my_favorite_color = "#112C85";
obj.do_something = function() {
// ...
};
function foo({ isCamelcased: no_camelcased }) {
// ...
}
var { category_id = 1 } = query;
// Good:
import { no_camelcased as camelCased } from "external-module";
var myFavoriteColor = "#112C85";
var _myFavoriteColor = "#112C85";
var myFavoriteColor_ = "#112C85";
var MY_FAVORITE_COLOR = "#112C85";
var { category_id: category } = query;
function foo({ isCamelCased }) {
// ...
};
function foo({ isCamelCased: isAlsoCamelCased }) {
// ...
}
var { categoryId = 1 } = query;
用有意义且常用的单词命名
也要注意拼写问题,避免拼写错误,推荐VS扩展 Code Spell Checker
// Bad:
const yyyymmdstr = moment().format('YYYY/MM/DD');
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\]+[,\s]+(.+?)s*(d{5})?$/;
saveCityZipCode(
address.match(cityZipCodeRegex)[1], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
address.match(cityZipCodeRegex)[2], // 这个公式到底要干嘛,对不起,原作者已经离职了。自己看代码
);
// Good:
const currentDate = moment().format('YYYY/MM/DD');
const address = 'One Infinite Loop, Cupertino 95014';
const cityZipCodeRegex = /^[^,\]+[,\s]+(.+?)s*(d{5})?$/;
const [, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);
避免无意义的变量名前缀
例如:如果创建了一个对象car,那么就没有必要把他的颜色命名为carColor
// Bad:
const car = {
carMake: 'Honda',
carModel: 'Accord',
carColor: 'Blue'
};
function paintCar( car ) {
car.carColor = 'Red';
}
// Good:
const car = {
make: 'Honda',
model: 'Accord',
color: 'Blue'
};
function paintCar( car ) {
car.color = 'Red';
}
传参使用默认值
es6的新feature 函数扩展与解构赋值默认值
// Bad:
function createMicrobrewery( name ) {
const breweryName = name || 'Hipster Brew Co.';
// ...
}
// Good:
function createMicrobrewery( name = 'Hipster Brew Co.' ) {
// ...
}
变量赋值考虑好取不到的情况
// Bad:
const MIN_NAME_LENGTH = 8;
let lastName = fullName[1];
if(lastName.length > MIN_NAME_LENGTH) {
// 这样你就给你的代码成功的埋了一个坑,你有考虑过如果fullName = ['jackie']这样的情况吗?
// 这样程序一跑起来就爆炸。要不你试试。
....
}
// Good:
const MIN_NAME_LENGTH = 8;
let lastName = fullName[1] || '';
// 做好兜底,fullName[1]中取不到的时候,不至于赋值个undefined,至少还有个空字符,从根本上讲,
// lastName的变量类型还是String,String原型链上的特性都能使用,不会报错。不会变成undefined。
if(lastName.length > MIN_NAME_LENGTH) {
....
}
// 其实在项目中有很多求值变量,对于每个求值变量都需要做好兜底。
let propertyValue = Object.attr || 0; // 因为Object.attr有可能为空,所以需要兜底。
// 但是,赋值变量就不需要兜底了。
let a = 2; // 因为有底了,所以不要兜着。
let myName = 'Tiny'; // 因为有底了,所以不要兜着。
2、抽取常量
所有无法一眼看清目的的数字都抽取常量
// Bad:
setTimeout( blastOff, 86400000 ); // 其他人知道 86400000 的意思吗?
if (value.length < 8) { // 为什么要小于8,8表示啥?
....
}
// Good:
const MILLISECOND_IN_A_DAY = 86400000;
setTimeout( blastOff, MILLISECOND_IN_A_DAY );
const MAX_INPUT_LENGTH = 8;
if (value.length < MAX_INPUT_LENGTH) { // 一目了然,不能超过最大输入长度
....
}
全局常量统一管理
1、保证可读性, 把数字变成有意义的常量,可以直接看懂你要传的1 是啥意思。
2、方便统一管理。 这个其实更重要。 有些后端也在用的映射关系,
举个例子比如说什么
1:未审核,2:合格:3,不合格, 同一业务的不同页面里很多地方都用了,那就在const里面维护一个映射关系就行了, 所有用到这个状态的地方,都从const里面取。
因为有这种后面改映射的情况, 比如果说3不用做不合格了, 要改成5, 要是不统一管理, 前端改动的成本可能就非常高了。
再次强调使用正确的单词、缩写
// Bad:
let fName = 'jackie'; // 看起来命名挺规范,缩写,驼峰法都用上,ESlint各种检测规范的工具都通过,But,fName是啥?
let lName = 'willen'; // 这个问题和上面的一样
// Good:
let firstName = 'jackie'; // 怎么样,是不是一目了然。少被喷了一次
let lastName = 'willen';
3、函数
从函数命知返回值类型
// Bad:
function showFriend() {
... // 完全想不到这个函数要干嘛,不知道返回的是啥
}
// Good:
function getFriendList() { ...} // 可以知道返回值是一个数组
// 对于返回true or false的函数,最好以should/is/can/has开头
function isEmpty() {...}
function canCreateDocuments() {...}
function hasLicense() {...}
// 也有一些特殊的常用函数
function renderTableHeader() {
... // return a reactNode
}
函数传参最好是对象而非位置映射
更不要混合使用,前面是对象,接着就又是位置映射。
// Bad:
page.getSVG(api, true, false); // true和false啥意思,一目不了然
// Good:
page.getSVG({
imageApi: api,
includePageBackground: true, // 一目了然,知道这些true和false是啥意思
compress: false,
})
一个方法只做一件事情
这是一条在软件工程领域流传久远的规则。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用。
// Bad:
function sendEmailToClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client)
if (clientRecord.isActive()) {
email(client)
}
})
}
// Good:
function sendEmailToActiveClients(clients) { //各个击破,易于维护和复用
clients.filter(isActiveClient).forEach(email)
}
function isActiveClient(client) {
const clientRecord = database.lookup(client)
return clientRecord.isActive()
}
删除不用的代码
你自己的代码,PM说这次版本不用了,不要注释掉,直接删除!不要相信他们“以后还会用的”这种话,记住历史代码这件事我们已经交给git了,你要做的是保证当前使用代码的整洁。
4、文件
@ 引用优化
文件的引入三层及以上用 @ 进行引用优化
// 各个项目的根目录 jsconfig.json都有配置
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
// Bad:
import { removeExcellentAlbum } from '../../../../actions/AlbumActions';
// Good:
import { openActionSheet } from '@/actions/CommonActions';
5、写法
异常逻辑处理
渲染函数如果有return null的情况要优先返回提升可读性【判断的异常条件个数<=2】。
// Bad:
const renderModal = () => {
if(isNeedShow) {
return(
<div>
......
......
<div>
)
}
return null
}
// Good:
const renderModal = () => {
if(!isNeedShow) {
return null
}
return(
<div>
......
......
</div>
)
}
三目运算符
三目两个语句体的内容不宜过大[不回行,不超长度]。
// Bad:
render(){
return(
<li>
{
isShowTitle
?(
<Title
...
...
/>
)
:(
<Notitle
...
...
/>
)
}
</li>
)
}
// Good:
① 简写内容
render(){
return(
<li>{isShowTitle?‘标题’:‘无标题’}</li>
)
}
② 抽取渲染函数
render(){
return(
<li>{isShowTitle? renderTitle():renderNoTitle}</li>
)
}
三目的条件语句中的条件个数不超过2个[超过的要抽取render函数]。
// Bad:
render(){
return(
<li>{(isShowTitle && titleList && titleList.length > 2) ? renderTitle():renderNoTitle}</li>
)
}
// Good:
const renderTitle = () => {
if(isShowTitle && titleList && titleList.length > 2) {
return(
<Title
...
...
/>
)
}
return (
<Notitle
...
...
/>
)
}
render(){
return(
<li>{renderTitle()}</li>
)
}
Redux 相关
①:Components 尽量设计为不需要 connect to store,连接store 的 component 应为 container。page 需要有组件的拆解,且原则上1个 component 文件长度最大不应超过500行。
②:ActionTypes 统一存放在 src/const/ActionTypes 里。不同的模块创建不同的 CT function 去给 action type name 加前缀,如:
const userCT = type => `@user/${type}`;
export const LOGIN = userCT('LOGIN');
③:使用 redux 的 connect 函数统一采用 @ (decorators) 的写法
@connect(mapStateToProps, mapDispatchToProps)
class CustomerComponent extends React.Componet {}
export default CustomerComponent;
其他部分
还有一些格式性质的问题,个人认为没有必要按照文档的方式维护,editor-config、prettier、eslint、stylelint,最后再加上husky校验,这些足以保证团队内代码格式一致,样式写法一致。
尾
当前规范并非全部是原创,特别是前半部分的用例,也参考了前人的经验,后面部分的规定更加偏向于原创。
当然这套组合可能并非非常适合你,比如命名方式可能在你的团队内更偏向于大驼峰甚至是下划线。
但是重要的是有了统一的风格,不论是阅读还是维护,绝对会让参与者有更好的“游戏体验”。