- 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 goodif (condition) doSomething();// goodif (condition) {doSomething();}
7. 空格
- 二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
// bad++ x;y ++;z = x?1:2;// good++x;y++;z = x ? 1 : 2;
- 用作代码块起始的左花括号 { 前必须有一个空格。
// badif (condition){}while (condition){}function funcName(){}// goodif (condition) {}while (condition) {}function funcName() {}
- if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
// badif(condition) {}while(condition) {}(function() {})();// goodif (condition) {}while (condition) {}(function () {})();
- 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。
// badvar obj = {a : 1,b:2,c :3};// goodvar obj = {a: 1,b: 2,c: 3};复制代码
8. 换行
- 每个独立语句结束后必须换行。
- 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
// badvar obj = {a: 1, b: 2, c: 3,};function test(){...}for (const key in object){if (object.hasOwnProperty(key)) {const element = object[key];}}// goodvar 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 不需要换行
// badif (condition) {...}else {...}try {...}catch (e) {...}finally {...}// goodif (condition) {...} else {...}try {...} catch (e) {...} finally {...}
9. 数组、对象
- 对象属性名不需要加引号;
- 对象以缩进的形式书写,不要写在一行;
- 数组最后不要有逗号。
- 对象最后要有逗号。
// badconst a = {'b': 1};const a = {b: 1};const a = {b: 1,c: 2};const arr = [1, 2, 3, 4,];// goodconst 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. 类型断言
// badfunction 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'.// badfunction getLength(something: string | number): number {if ((<string>something).length) {return (<string>something).length;} else {return something.toString().length;}}// goodfunction 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' }; // OKconst 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,
// badconstructor (){this.setState({ people: this.props.people })}// goodstate: IState = {people: {},};
16. 渲染默认值
- 添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
// badrender(){{name}}// goodrender(){{!!name || '--'}}复制代码
- 还有一种情况,就是本来后端应该返回一个数组给你,但是数据库取不到数据,可能后端给你返回了null,然后前端null.length。这样就gg了
// badconst { list, totalCount } = await getPeopleList(keyword, page, pageSize);list 可能是null或者undefinedlist.length将直接导致前端报错this.setState({status: STATUS.READY,apps: list,total: totalCount,page: page,});// goodconst { list, totalCount } = await getPeopleList(keyword, page, pageSize);this.setState({status: STATUS.READY,apps: list || [],total: totalCount || 0,page: page,});
17. 不确定的属性,最后却疯狂的用…访问不存在的属性
例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接data.dataList.forEach()
// badconst data = await getPeopleList(keyword, page, pageSize);data.dataList.forEach() // 直接挂了// goodconst data = await getPeopleList(keyword, page, pageSize);if (data && data.dataList && Array.isArray(data.dataList) {data.dataList.forEach()}
18. React简单组件可以使用函数代替
// badclass Listing extends React.Component {render() {return <div>{this.props.hello}</div>;}}// goodfunction Listing({ hello }) {return <div>{hello}</div>;}
19. 对于常用的属性进行缓存
// badthis.props.app.openid;this.state.time// goodconst { app } = this.props;const { time } = this.state;console.log(app.openid)
20. input 输入框使用 trim()
// badlet searchContent = form.search.value;// goodlet 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(() => {// TODOconsole.log('')return {a:300}})// 第二个参数,一般是用于在setState之后做一些操作this.setState({a:300}, () => {// TODO})
25. 在 componentWillUnmount 里面去除副作用的函数
- 清除 EventListener
- 中止数据请求
- 清除定时器
26. 循环时key的使用
- 对于组件中的 key 优化,起到最大化重用 dom
//badthis.state.dataAry.map((item, index) => {return <span key={index} />;});//goodthis.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的时候做了一些操作,让人不知所以然。
// badif (type !== 0) {// TODO}// goodconst STATUS: Record<string, any> = {READY: 0,FETCHING: 1,FAILED: 2};if (type === STATUS.READY) {// TODO}// bestenum STATUS {// 就绪READY = 0,// 请求中FETCHING = 1,// 请求失败FAILED = 2,}
29. 不要使用renderXXX,要使用函数式组件
发现团队一些小伙伴为了减少render函数里面的代码量,会把一些元素拆分到函数里面。
// badrenderHeader = () => {return (<div />)}renderBody = () => {return (<div />)}renderFooter = () => {return (<div />)}render(){return(<div>renderHeader()renderBody()renderFooter()</div>)}
更好的办法,是用函数式组件取代在当前组件里面写方法
// goodfunction 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
