阅读 818

前端大型项目系列(一) - 前端代码规范

前言

每个团队都应该有自己团队的代码规范,why?

1、保持代码风格统一,提高可读性

2、项目支援,团队协作,接入成本低

3、Code Review可以减少不少时间

命名规范来源

参考 百度前端代码规范阿里前端代码规范clean codecss命名规范-BEM、以及结合项目实战经验而来

一些争论抉择理由

一些争论及理由:
中划(-)线代替下划线(_)
 1、-只需一个按键,更方便
 2、关于下划线vim会提示问题,现在都会装iterm2 + zsh-autosuggestions
 
css 中在某些浏览器下用大小写和下划线会有问题,所以最常用的是中划线
复制代码

相关名词解释

Camel命名法: 小驼峰 // getUmitsDetailInfo
Pascal命名法: 大驼峰 // GetUmitsDetailInfo

复制代码

命名规范手册

规范概览

1、js:Camel命名法
2、css:BEM
3、文件夹:
    react组件文件夹: Pascal与组件名保持一致 // Umits
    非组件:两个单词-分隔, xx-xx //umits-plugin
4、文件: xx-xx  // umits-plugin
5、图片: xx-xx // umits-pic
复制代码

(有少部分特殊,参考下面详细命名规范)

一:文件夹、文件,命名规范

文件夹

1、名词 ,复数加s // components
2、多个单词组合加中划:xx-xx // umits-plugin
3、react组件:文件夹名字必须和组件名称保持一致,Pascal命名法 ,权重大于第二条
    // demo
        components
            UmitsInfo
                index.tsx // export UmitsInfo = () => {}
4、react高阶组件:文件夹名字必须和组件名称保持一致,采用withXxxxHoc
     // demo
        components
            withRequestCancelHoc
                index.tsx // export withRequestCancelHoc = () => {}
复制代码

文件

1、普通文件,单词加中划线(-)形式 // xx-xx
2、react组件命名: Pascal命名法
复制代码

二:js命名规范

【组件】PascalCase

  • index.tsx
export const UmitsInfo = () => {}

// 文件目录: components/UmitsInfo/index.tsx
// 注意这里文件夹名字和组件名字一致
复制代码

【高阶组件】with + Camel + Hoc (即:withXxxxHoc)

  • index.js
// demo
export const withCancelRequestHoc = () => {}

// 高阶组件使用withXxxxxHoc命名

why:
1、高阶组件事实上并非一个组件,而是一个“生成组件类型”的函数
2、因此遵守JavaScript函数命名的规范,使用camelCase命名
3、加上Hoc 更直观

复制代码

【组件属性】回调:onXxx,内部方法处理:handleXxx


// demo

 <div onClick={handleLogInfo}>内部使用</div>
 <div onClick={onLogInfo}>回调外面方法</div>

复制代码

【常量】全部字母大写,单词间下划线分隔

// demo
const UMITS_VERSION= 1.0.0

复制代码

【变量】,Camel命名规范

var umitsCli = {};

复制代码

【函数,函数参数】都用Camel命名法

function useUmits(currentVersion:number) {
}

复制代码

【构造函数】Pascal命名法

calss GetUmitsInfo() {}

复制代码

【类,名词】Pascal命名法

function UmitsInfo() {
}

复制代码

【枚举】Pascal命名法,枚举的属性使用全部字母大写,单词间下划线分隔

enum VersionInfo  {
    VERSION_1=1
    VERSION_2=2
}


复制代码

【函数名,动宾短语】

function useUmits() {}
复制代码

【boolean类型】使用is开头(has开头也行,umits统一使用is开头)

const isReady = false

复制代码

【Promise对象】使用动宾短语进行时

let loadingData = axiosGet('/umits/info')


复制代码

三:js代码书写推荐

以项目配置好的eslint、stylelint为准,lint没有覆盖到的推荐下面

变量交换

// bad
let temp = x;
x = y;
y = temp;

// good
[x, y] = [y, x];



复制代码

函数参数默认值

// bad
function foo(text) {
    text = text || 'hello';
}

// good
function foo(text = 'hello') {
}

复制代码

函数参数


// bad
function foo() {
    console.log([].join.call(arguments));
}

// good
function foo(...args) {
    console.log(args.join(''));
}


复制代码

IIFE,函数表达式外添加括号

// bad
var test = function () {
    // Code
    return result;
}();

// good
var test = (function () {
   // Code
   return result;
})();

复制代码

如何提高读性

// bad
// 1,2,3是撒一脸懵逼
if( [1,2,3].includes(status) ){
    ...
}

//good
const ActivityStatus = {
    1: 'START',
    2: 'END ',
    3: 'PENDING',
};
const isShowButton = ['START', 'PENDING'].includes(ActivityStatus[status]);
if (isShowButton) {
    console.log(1);
}


复制代码

如何提高可读性,更多的细节请参考clean code ,强烈推荐:clean code !!! 授人以鱼,不如授之以渔

使用设计模式-策略模式,代替if else

// bad

const buy = () => {
    console.log('buy');
};
const sale = () => {
    console.log('sale');
};
const eat = () => {
    console.log('eat');
};

const test = (type: any) => {
    if (type === 'buy') {
        buy();
    } else if (type == sale) {
        sale();
    }ese if(type === 'eat'){
    	eat()
    }
};


// good
const strategy = {
    buy: () => {
        console.log('buy');
    },
    sale: () => {
        console.log('sale');
    },
    eat: = () => {
    	console.log('eat');
    }

};
const test = (type: Type) => {
   strategy[type] && strategy[type]();
};


复制代码

善用工具lodash,做好数据兜底

let obj = {
    info:{
        detail:{
            names:[]
        }
    }
}
// bad
obj.info &&  obj.info.detail && obj.info.detail.names.map( name  => {
    
} )

// good
import _ from 'lodash'
const names = _.get(obj,'info.detail.age',[])
names.map( name => {} )


复制代码

四:css规范

css命名规范,参考BEM

BEM是什么

block:块,也可以叫做组件,模块
element:元素,父子关系
modifier:修饰符,当前状态,不能单独使用,一定要依赖于块或元素

格式:

.block {}

.block__element {}

.block__element--disabled {}

.block-two__element--disabled {}

 -中划线 :块之间单词连接。

__ 双下划线:双下划线用来连接块和块的子元素

-- 双中划线:描述一个块或者块的子元素的一种状态

复制代码
  • BEM命名demo
<div class="wrap">
    <div class="wrap__body">
        <button class="wrap__button--primary"></button>
        <button class="wrap__button--success"></button>
    </div>
</div>




复制代码

类名,ID,如何命名

1、类名统一用中划线作为类名,id 采用驼峰式命名

【例子】

<div className="demo-head" id="demoHead">
    demo
</div>

复制代码

嵌套不要超过三级

index.less

// bad
.info{
    .detail{
        .names{
            .name
        }
    }
}


复制代码

less书写规范,严格到css属性顺序

强制遵循stylelint

  • 1、安装styleling相关依赖包:
npm i stylelint stylelint-config-prettier stylelint-config-standard stylelint-order -D

复制代码
  • 2、vscode安装stylelint插件
  • 3、配置stylelint.config.js
/*
 * @Author: xxx@baidu.com
 * @Date: 2021-01-05 19:06:17
 * @LastEditTime: 2021-01-05 19:46:19
 * @LastEditors: xxx@baidu.com
 * @FilePath: /umits-didi/stylelint.config.js
 * @! Description: !
 */
module.exports = {
    root: true,
    plugins: ['stylelint-order'],
    extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
    rules: {
        'max-empty-lines': 4,
        'selector-pseudo-class-no-unknown': [
            true,
            {
                ignorePseudoClasses: ['global'],
            },
        ],
        'at-rule-no-unknown': [
            true,
            {
                ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin', 'for'],
            },
        ],
        'no-duplicate-selectors': null,
        'no-empty-source': null,
        'unicode-bom': 'never',
        'no-descending-specificity': null,
        'font-family-no-missing-generic-family-keyword': null,
        'declaration-colon-space-after': 'always-single-line',
        'declaration-colon-space-before': 'never',
        'declaration-block-trailing-semicolon': 'always',
        'rule-empty-line-before': [
            'always',
            {
                ignore: ['after-comment', 'first-nested'],
            },
        ],
        'property-no-unknown': [
            true,
            {
                ignoreProperties: ['lines'],
            },
        ],
        'media-feature-name-no-unknown': [
            true,
            {
                ignoreMediaFeatureNames: 'min-device-pixel-ratio',
            },
        ],
        'unit-no-unknown': [
            true,
            {
                ignoreUnits: ['rpx'],
            },
        ],
        'selector-pseudo-element-no-unknown': [
            true,
            {
                ignorePseudoElements: ['v-deep'],
            },
        ],
        // 指定声明块内属性的字母顺序
        'order/properties-order': [
            'position',
            'top',
            'right',
            'bottom',
            'left',
            'z-index',
            'display',
            'float',
            'width',
            'height',
            'max-width',
            'max-height',
            'min-width',
            'min-height',
            'padding',
            'padding-top',
            'padding-right',
            'padding-bottom',
            'padding-left',
            'margin',
            'margin-top',
            'margin-right',
            'margin-bottom',
            'margin-left',
            'margin-collapse',
            'margin-top-collapse',
            'margin-right-collapse',
            'margin-bottom-collapse',
            'margin-left-collapse',
            'overflow',
            'overflow-x',
            'overflow-y',
            'clip',
            'clear',
            'font',
            'font-family',
            'font-size',
            'font-smoothing',
            'osx-font-smoothing',
            'font-style',
            'font-weight',
            'hyphens',
            'src',
            'line-height',
            'letter-spacing',
            'word-spacing',
            'color',
            'text-align',
            'text-decoration',
            'text-indent',
            'text-overflow',
            'text-rendering',
            'text-size-adjust',
            'text-shadow',
            'text-transform',
            'word-break',
            'word-wrap',
            'white-space',
            'vertical-align',
            'list-style',
            'list-style-type',
            'list-style-position',
            'list-style-image',
            'pointer-events',
            'cursor',
            'background',
            'background-attachment',
            'background-color',
            'background-image',
            'background-position',
            'background-repeat',
            'background-size',
            'border',
            'border-collapse',
            'border-top',
            'border-right',
            'border-bottom',
            'border-left',
            'border-color',
            'border-image',
            'border-top-color',
            'border-right-color',
            'border-bottom-color',
            'border-left-color',
            'border-spacing',
            'border-style',
            'border-top-style',
            'border-right-style',
            'border-bottom-style',
            'border-left-style',
            'border-width',
            'border-top-width',
            'border-right-width',
            'border-bottom-width',
            'border-left-width',
            'border-radius',
            'border-top-right-radius',
            'border-bottom-right-radius',
            'border-bottom-left-radius',
            'border-top-left-radius',
            'border-radius-topright',
            'border-radius-bottomright',
            'border-radius-bottomleft',
            'border-radius-topleft',
            'content',
            'quotes',
            'outline',
            'outline-offset',
            'opacity',
            'filter',
            'visibility',
            'size',
            'zoom',
            'transform',
            'box-align',
            'box-flex',
            'box-orient',
            'box-pack',
            'box-shadow',
            'box-sizing',
            'table-layout',
            'animation',
            'animation-delay',
            'animation-duration',
            'animation-iteration-count',
            'animation-name',
            'animation-play-state',
            'animation-timing-function',
            'animation-fill-mode',
            'transition',
            'transition-delay',
            'transition-duration',
            'transition-property',
            'transition-timing-function',
            'background-clip',
            'backface-visibility',
            'resize',
            'appearance',
            'user-select',
            'interpolation-mode',
            'direction',
            'marks',
            'page',
            'set-link-source',
            'unicode-bidi',
            'speak',
        ],
    },
};



复制代码

less文件位置

less公共文件放在:style/less/common下
sytle/less/common/color.less

复制代码

@import ,less变量申明顺序

1、@import;
2、变量声明;
3、样式声明;

例子:

@import "mixins/you.less";
@default-you-color: #444;

.demo {
  width: 22px;
}
复制代码

五:注释规范

注释插件:koroFileHeader

公共组件,业务组件,头部注释【强制】

/*
 * @Author: your name
 * @Date: 2020-12-09 19:52:35
 * @LastEditTime: 2020-12-13 16:05:52
 * @LastEditors: xxx@xx.com
 * @Description: 这个文件夹作用是什么
 */

关于【Author】
1、 按照【责任】排序,有问题直接招第一人更有效
2、 不允许删除,任何劳动成果都应该都被尊重
复制代码

公共方法,注释【强制】

 /**
 * @Author: xxx@baidu.com
 * @LastEditors: xxx@baidu.com
 * @description: 将数据格式化符合antd select组件data所需格式
 * @return { [{ value:string, text:string }] } 
 * @param { [{ val:string, desc:string }]} data:后端返回数据
 */
   
formatDataToAntdSelectData(data: any) {
    return data.map((item: Enum) => {
        return {
            value: item && item.value,
            text: item && item.desc,
        };
    });
},

复制代码

api,注释【强制】

// 获取用户信息
getUserInfo: (params:{ id:number }) => {
  return axiosGet('/user/get', params)
},


复制代码

函数,对象,常量,注释【非强制】

根据clean code原则,最好的注释就是没有注释:TS + 语义法命名 ,可以覆盖大部分场景,但是存在业务复杂,函数逻辑复杂的地方需要有注释

bad:
 /**
 * @description: 
 * @param {string} age 年龄
 * @param {string} height 身高
 * @return {*}
 */
// 这种一看就知道什么意思增加注释只会降低整个文件的可读性
function userInfo(age:number,height:number){}

good:
  /**
 * @Author: xxx@baidu.com
 * @LastEditors: xxx@baidu.com
 * @description: 将数据格式化符合antd select组件data所需格式
 * @return { [{ value:string, text:string }] } 
 * @param { [{ val:string, desc:string }]} data:后端返回数据
 */
 
// 这种业务比较复杂的必须加注释  
formatDataToAntdSelectData(data: any) {
    return data.map((item: Enum) => {
        return {
            value: item && item.value,
            text: item && item.desc,
        };
    });
},

复制代码

六:常用命名单词整理

增删改查统一用如下单词

单词语意
add新建
update更新
delete删除
get获取信息

函数常用动词

单词语意单词语意
get获取set设置
add增加remove删除
create创建destory移除
start启动stop停止
open打开close关闭
read读取write写入
load载入save保存
create创建destroy销毁
begin开始end结束
backup备份restore恢复
import导入export导出
split分割merge合并
inject注入extract提取
attach附着detach脱离
bind绑定separate分离
view查看browse浏览
edit编辑modify修改
select选取mark标记
copy复制paste粘贴,
undo撤销redo重做
insert插入delete移除
add加入append添加
clean清理clear清除
index索引sort排序
find查找search搜索
increase增加decrease减少
play播放pause暂停
launch启动run运行
compile编译execute执行
debug调试trace跟踪
observe观察listen监听
build构建publish发布
input输入output输出
encode编码decode解码
encrypt加密decrypt解密
compress压缩decompress解压缩
pack打包unpack解包
parse解析emit生成
connect连接disconnect断开
send发送receive接收
download下载upload上传
refresh刷新synchronize同步
update更新revert复原
lock锁定unlock解锁
submit提交commit交付
pushpull
expand展开collapse折叠
begin起始end结束
start开始finish完成
enter进入exit退出
abort放弃quit离开
obsolete废弃depreciate废旧
collect收集aggregate聚集

参考资料:

CSS — BEM 命名规范

从支付宝tabbar看BEM

百度前端代码规范

clean-code-js

阿里前端代码规范

文章分类
前端
文章标签