前言
每个团队都应该有自己团队的代码规范,why?
1、保持代码风格统一,提高可读性
2、项目支援,团队协作,接入成本低
3、Code Review可以减少不少时间
命名规范来源
参考 百度前端代码规范、阿里前端代码规范、clean code、css命名规范-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 | 交付 |
| push | 推 | pull | 拉 |
| expand | 展开 | collapse | 折叠 |
| begin | 起始 | end | 结束 |
| start | 开始 | finish | 完成 |
| enter | 进入 | exit | 退出 |
| abort | 放弃 | quit | 离开 |
| obsolete | 废弃 | depreciate | 废旧 |
| collect | 收集 | aggregate | 聚集 |
参考资料: