- 1. 注释
- 2. 引用组件顺序
- 3. 引号
- 4. 缩进
- 5. 分号
- 6. 括号
- 7. 空格
- 8. 换行
- 9. 数组、对象
- 10. 命名
- 11. 类型断言
- 12. interface声明顺序
- 13. ts好用的相关工具泛型
- 14. ts一些好用的小tips
- 15. 仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state,
- 16. 渲染默认值
- 17. 不确定的属性,最后却疯狂的用…访问不存在的属性
- 18. React简单组件可以使用函数代替
- 19. 对于常用的属性进行缓存
- 20. input 输入框使用 trim()
- 21. 使用 typescript 与 react-router规范
- 22. 组件嵌套过深
- 23. 代码过滤掉你没考虑到的情况
- 24. setState有三种用法
- 25. 在 componentWillUnmount 里面去除副作用的函数
- 26. 循环时key的使用
- 27. 在组件中获取真实 dom
- 28. 减少魔法数字
- 29. 不要使用renderXXX,要使用函数式组件
- 30. void 0 替代undefined
- 31. 代码整洁
- 32. interface 定义规范
1. 注释
(1) 文件顶部的注释,包括描述、作者、日期
/**
* @description xxxxxx
* @author daoyi
* @since 19/05/21
*/
(2) 模块的注释
/**
* 拷贝数据
* @param {*} data 要拷贝的源数据
* @param {boolean} [isDeep=false] 是否深拷贝,默认浅拷贝
* @return {*} 返回拷贝后的数据
*/
复制代码
(3) 业务代码注释
/*业务代码注释*/
(4) 变量注释
interface IState {
// 名字
name: string;
// 电话
phone: number;
// 地址
address: string;
}
2. 引用组件顺序
- 先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 scss 样式
import * as React from 'react';
import { Dropdown, Menu, Icon } from 'antd';
import Header from './Header';
import toast from 'common/toast';
import './index.scss';
3. 引号
- 使用单引号,或者 es6 的反引号
4. 缩进
- 使用两个空格
const handleCheck = () => {
onCancel && onCancel();
onClose && onClose();
};
5. 分号
- 除了代码块的以外的每个表达式后必须加分号。
6. 括号
下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try, catch, finally, with。
// not good
if (condition) doSomething();
// good
if (condition) {
doSomething();
}
7. 空格
- 二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
// bad
++ x;
y ++;
z = x?1:2;
// good
++x;
y++;
z = x ? 1 : 2;
- 用作代码块起始的左花括号 { 前必须有一个空格。
// bad
if (condition){
}
while (condition){
}
function funcName(){
}
// good
if (condition) {
}
while (condition) {
}
function funcName() {
}
- if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
// bad
if(condition) {
}
while(condition) {
}
(function() {
})();
// good
if (condition) {
}
while (condition) {
}
(function () {
})();
- 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。
// bad
var obj = {
a : 1,
b:2,
c :3
};
// good
var obj = {
a: 1,
b: 2,
c: 3
};
复制代码
8. 换行
- 每个独立语句结束后必须换行。
- 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
// bad
var obj = {
a: 1
, b: 2
, c: 3,
};
function test()
{
...
}
for (const key in object)
{
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}
// good
var obj = {
a: 1,
b: 2,
c: 3,
};
function test() {
...
}
for (const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
}
}
- 下列关键字后:else, catch, finally 不需要换行
// bad
if (condition) {
...
}
else {
...
}
try {
...
}
catch (e) {
...
}
finally {
...
}
// good
if (condition) {
...
} else {
...
}
try {
...
} catch (e) {
...
} finally {
...
}
9. 数组、对象
- 对象属性名不需要加引号;
- 对象以缩进的形式书写,不要写在一行;
- 数组最后不要有逗号。
- 对象最后要有逗号。
// bad
const a = {
'b': 1
};
const a = {b: 1};
const a = {
b: 1,
c: 2
};
const arr = [1, 2, 3, 4,];
// good
const a = {
b: 1,
c: 2,
};
const arr = [1, 2, 3, 4];
10. 命名
- 类名: 大驼峰式风格,字母和数字,例如:AbcTest。禁止汉字、特殊符号,禁止非大驼峰式风格。
- 函数名: 小驼峰式风格,例如:onXXXEvent。禁止汉字、特殊符号,禁止非小驼峰式风格,命名尽量见名知意。
- 变量名: 同函数名。
- 常量: 全大写风格,大写字母、数字和下划线,单词之间以下划线分隔,例如:ABC_TEST。禁止汉字、特殊符号、小写字母。
- 使用 onXxx 形式作为 props 中用于回调的属性名称。
- class命名: 推荐使用BEM命名(block element modify),块与块之间使用-,元素与元素之间使用,修饰符前使用—,一般一个类名中只出现一次, 如 “block-block__element—modify”
interface IProps {
onClose?: () => void;
onOk?: (item: Record<string, any>) => void;
}
- 组件内的事件函数使用 handle 开头尾,handleCheckBtn。
- 使用 withXxx 形式的词作为高阶组件的名称。
- 接口命名前面带上 I 表示 interface
interface IProps {}
interface IState {}
11. 类型断言
// bad
function getLength(something: string | number): number {
return something.length;
}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
// bad
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
// good
function getLength(something: string | number): number {
if (typeof something === 'string') {
return something.length;
} else {
return something.toString().length;
}
}
复制代码
12. interface声明顺序
日常用到比较多的是四种,只读参数放第一位,必选参数第二位,可选参数次之,不确定参数放最后
interface iProps {
readonly x: number;
readonly y: number;
name: string;
age: number;
height?: number;
[propName: string]: any;
}
复制代码
13. ts好用的相关工具泛型
- Record
用这个来声明对象结构的类型
用于定义一个javascript的对象,key是字符串,value是任意类型
const people:Record<string,any> = {
name: 'chengfeng',
age: 10
}
复制代码
- Partial 作用是将传入的属性变为可选项.
interface iPeople {
title: string;
name: string;
}
const people: Partial<iPeople> = {
title: 'Delete inactive users',
};
定义的结构可以是接口iPeople的任意key
复制代码
- Readonly 作用是将传入的属性变为变成只读
interface iPeople {
title: string;
name: string;
}
const people: Readonly<Todo> = {
title: 'todo list',
name: chenfeng;
};
title name属性就是只读的了
复制代码
- Required 的作用是将传入的属性变为必选项
interface iPeople {
title?: string;
name?: string;
}
const people1: Props = { title: 'ts' }; // OK
const people22: Required<iPeople> = { title: 'ts' }; // Error: property 'name' missing
14. ts一些好用的小tips
- keyof
interface iPeople {
name: string;
age: number
}
type T = keyof iPeople // -> "name" | "age"
- in
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any }
15. 仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state,
// bad
constructor (){
this.setState({ people: this.props.people })
}
// good
state: IState = {
people: {},
};
16. 渲染默认值
- 添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
// bad
render(){
{name}
}
// good
render(){
{!!name || '--'}
}
复制代码
- 还有一种情况,就是本来后端应该返回一个数组给你,但是数据库取不到数据,可能后端给你返回了null,然后前端null.length。这样就gg了
// bad
const { list, totalCount } = await getPeopleList(keyword, page, pageSize);
list 可能是null或者undefined
list.length将直接导致前端报错
this.setState({
status: STATUS.READY,
apps: list,
total: totalCount,
page: page,
});
// good
const { list, totalCount } = await getPeopleList(keyword, page, pageSize);
this.setState({
status: STATUS.READY,
apps: list || [],
total: totalCount || 0,
page: page,
});
17. 不确定的属性,最后却疯狂的用…访问不存在的属性
例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接data.dataList.forEach()
// bad
const data = await getPeopleList(keyword, page, pageSize);
data.dataList.forEach() // 直接挂了
// good
const data = await getPeopleList(keyword, page, pageSize);
if (data && data.dataList && Array.isArray(data.dataList) {
data.dataList.forEach()
}
18. React简单组件可以使用函数代替
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
19. 对于常用的属性进行缓存
// bad
this.props.app.openid;
this.state.time
// good
const { app } = this.props;
const { time } = this.state;
console.log(app.openid)
20. input 输入框使用 trim()
// bad
let searchContent = form.search.value;
// good
let searchContent = form.search.value.trim();
21. 使用 typescript 与 react-router规范
- 继承React.Component类的时候,由于自定义的props上没有history 和 match等字段,则需要在自定义props的时候继承RouteComponentProps
- export component时需要使用withRouter()方法处理
// interface中
import { RouteComponentProps } from 'react-router';
interface IProps extends RouteComponentProps {
// id: number;
}
// tsx中
import { withRouter, RouteComponentProps } from 'react-router-dom';
class App extends React.Component<IProps & IState> {
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(EmployeeListComponent));
22. 组件嵌套过深
- 组件一般不要超过三层,最多四层,层级过深可能会导致数据传递过深,在做一些颗粒度比较细的操作的时候,处理起来较为繁琐,可以使用 redux 等状态管理工具替代。
23. 代码过滤掉你没考虑到的情况
- 例如一个函数,你只想操作字符串,那你必须在函数开头就只允许参数是字符串
function parse (str:string){
if (typeof(str) === 'string' ) {
}
}
24. setState有三种用法
// 对象
this.setState({
})
// 函数,一般是用于在setState之前做一些操作
this.setState(
() => {
// TODO
console.log('')
return {
a:300
}
}
)
// 第二个参数,一般是用于在setState之后做一些操作
this.setState({
a:300
}, () => {
// TODO
})
25. 在 componentWillUnmount 里面去除副作用的函数
- 清除 EventListener
- 中止数据请求
- 清除定时器
26. 循环时key的使用
- 对于组件中的 key 优化,起到最大化重用 dom
//bad
this.state.dataAry.map((item, index) => {
return <span key={index} />;
});
//good
this.state.dataAry.map(item => <span key={item.id} />);
27. 在组件中获取真实 dom
- 使用 16 版本后的 createRef()函数
class MyComponent extends React.Component<iProps, iState> {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
this.inputRef.current.focus();
}
}
28. 减少魔法数字
- 写代码的时候尽量减少一些未知含义的数字,尽量用英文单词。例如type === 0的时候做了一些操作,让人不知所以然。
// bad
if (type !== 0) {
// TODO
}
// good
const STATUS: Record<string, any> = {
READY: 0,
FETCHING: 1,
FAILED: 2
};
if (type === STATUS.READY) {
// TODO
}
// best
enum STATUS {
// 就绪
READY = 0,
// 请求中
FETCHING = 1,
// 请求失败
FAILED = 2,
}
29. 不要使用renderXXX,要使用函数式组件
发现团队一些小伙伴为了减少render函数里面的代码量,会把一些元素拆分到函数里面。
// bad
renderHeader = () => {
return (<div />)
}
renderBody = () => {
return (<div />)
}
renderFooter = () => {
return (<div />)
}
render(){
return(
<div>
renderHeader()
renderBody()
renderFooter()
</div>
)
}
更好的办法,是用函数式组件取代在当前组件里面写方法
// good
function RenderHeader(props) = {
return (<div />)
}
function RenderBody(props) = {
return (<div />)
}
function RenderFooter(props) = {
return (<div />)
}
class Component extends React.Component<iProps, iState>{
render () {
return(
<div>
<RenderHeader />
<RenderBody />
<RenderFooter />
</div>
)
}
}
30. void 0 替代undefined
clearSessioin = () => {
req.session.userName = undefined;
req.session.userName = void 0
}
31. 代码整洁
- 未使用到的方法和import的文件,上传版本之前要删除
32. interface 定义规范
- interface应该定义在对应的namespace中, namespace命名 N+模块名(可多个)+功能名+namespaces 如:NLoginInfoNamespaces
- interface 接口请求参数定义 I+模块名(可多个)+功能名+Params 如:IUserInfoParams