1. 注释

(1) 文件顶部的注释,包括描述、作者、日期

  1. /**
  2. * @description xxxxxx
  3. * @author daoyi
  4. * @since 19/05/21
  5. */

(2) 模块的注释

  1. /**
  2. * 拷贝数据
  3. * @param {*} data 要拷贝的源数据
  4. * @param {boolean} [isDeep=false] 是否深拷贝,默认浅拷贝
  5. * @return {*} 返回拷贝后的数据
  6. */
  7. 复制代码

(3) 业务代码注释

  1. /*业务代码注释*/

(4) 变量注释

  1. interface IState {
  2. // 名字
  3. name: string;
  4. // 电话
  5. phone: number;
  6. // 地址
  7. address: string;
  8. }

2. 引用组件顺序

  • 先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 scss 样式
  1. import * as React from 'react';
  2. import { Dropdown, Menu, Icon } from 'antd';
  3. import Header from './Header';
  4. import toast from 'common/toast';
  5. import './index.scss';

3. 引号

  • 使用单引号,或者 es6 的反引号

4. 缩进

  • 使用两个空格
  1. const handleCheck = () => {
  2. onCancel && onCancel();
  3. onClose && onClose();
  4. };

5. 分号

  • 除了代码块的以外的每个表达式后必须加分号。

6. 括号

下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try, catch, finally, with。

  1. // not good
  2. if (condition) doSomething();
  3. // good
  4. if (condition) {
  5. doSomething();
  6. }

7. 空格

  • 二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
  1. // bad
  2. ++ x;
  3. y ++;
  4. z = x?1:2;
  5. // good
  6. ++x;
  7. y++;
  8. z = x ? 1 : 2;
  • 用作代码块起始的左花括号 { 前必须有一个空格。
  1. // bad
  2. if (condition){
  3. }
  4. while (condition){
  5. }
  6. function funcName(){
  7. }
  8. // good
  9. if (condition) {
  10. }
  11. while (condition) {
  12. }
  13. function funcName() {
  14. }
  • if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格。
  1. // bad
  2. if(condition) {
  3. }
  4. while(condition) {
  5. }
  6. (function() {
  7. })();
  8. // good
  9. if (condition) {
  10. }
  11. while (condition) {
  12. }
  13. (function () {
  14. })();
  • 在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格。
  1. // bad
  2. var obj = {
  3. a : 1,
  4. b:2,
  5. c :3
  6. };
  7. // good
  8. var obj = {
  9. a: 1,
  10. b: 2,
  11. c: 3
  12. };
  13. 复制代码

8. 换行

  • 每个独立语句结束后必须换行。
  • 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
  1. // bad
  2. var obj = {
  3. a: 1
  4. , b: 2
  5. , c: 3,
  6. };
  7. function test()
  8. {
  9. ...
  10. }
  11. for (const key in object)
  12. {
  13. if (object.hasOwnProperty(key)) {
  14. const element = object[key];
  15. }
  16. }
  17. // good
  18. var obj = {
  19. a: 1,
  20. b: 2,
  21. c: 3,
  22. };
  23. function test() {
  24. ...
  25. }
  26. for (const key in object) {
  27. if (object.hasOwnProperty(key)) {
  28. const element = object[key];
  29. }
  30. }
  • 下列关键字后:else, catch, finally 不需要换行
  1. // bad
  2. if (condition) {
  3. ...
  4. }
  5. else {
  6. ...
  7. }
  8. try {
  9. ...
  10. }
  11. catch (e) {
  12. ...
  13. }
  14. finally {
  15. ...
  16. }
  17. // good
  18. if (condition) {
  19. ...
  20. } else {
  21. ...
  22. }
  23. try {
  24. ...
  25. } catch (e) {
  26. ...
  27. } finally {
  28. ...
  29. }

9. 数组、对象

  • 对象属性名不需要加引号;
  • 对象以缩进的形式书写,不要写在一行;
  • 数组最后不要有逗号。
  • 对象最后要有逗号。
  1. // bad
  2. const a = {
  3. 'b': 1
  4. };
  5. const a = {b: 1};
  6. const a = {
  7. b: 1,
  8. c: 2
  9. };
  10. const arr = [1, 2, 3, 4,];
  11. // good
  12. const a = {
  13. b: 1,
  14. c: 2,
  15. };
  16. const arr = [1, 2, 3, 4];

10. 命名

  • 类名: 大驼峰式风格,字母和数字,例如:AbcTest。禁止汉字、特殊符号,禁止非大驼峰式风格。
  • 函数名: 小驼峰式风格,例如:onXXXEvent。禁止汉字、特殊符号,禁止非小驼峰式风格,命名尽量见名知意。
  • 变量名: 同函数名。
  • 常量: 全大写风格,大写字母、数字和下划线,单词之间以下划线分隔,例如:ABC_TEST。禁止汉字、特殊符号、小写字母。
  • 使用 onXxx 形式作为 props 中用于回调的属性名称。
  • class命名: 推荐使用BEM命名(block element modify),块与块之间使用-,元素与元素之间使用,修饰符前使用—,一般一个类名中只出现一次, 如 “block-block__element—modify”
  1. interface IProps {
  2. onClose?: () => void;
  3. onOk?: (item: Record<string, any>) => void;
  4. }
  • 组件内的事件函数使用 handle 开头尾,handleCheckBtn。
  • 使用 withXxx 形式的词作为高阶组件的名称。
  • 接口命名前面带上 I 表示 interface
  1. interface IProps {}
  2. interface IState {}

11. 类型断言

  1. // bad
  2. function getLength(something: string | number): number {
  3. return something.length;
  4. }
  5. // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
  6. // Property 'length' does not exist on type 'number'.
  7. // bad
  8. function getLength(something: string | number): number {
  9. if ((<string>something).length) {
  10. return (<string>something).length;
  11. } else {
  12. return something.toString().length;
  13. }
  14. }
  15. // good
  16. function getLength(something: string | number): number {
  17. if (typeof something === 'string') {
  18. return something.length;
  19. } else {
  20. return something.toString().length;
  21. }
  22. }
  23. 复制代码

12. interface声明顺序

日常用到比较多的是四种,只读参数放第一位,必选参数第二位,可选参数次之,不确定参数放最后

  1. interface iProps {
  2. readonly x: number;
  3. readonly y: number;
  4. name: string;
  5. age: number;
  6. height?: number;
  7. [propName: string]: any;
  8. }
  9. 复制代码

13. ts好用的相关工具泛型

  • Record 用这个来声明对象结构的类型
  1. 用于定义一个javascript的对象,key是字符串,value是任意类型
  2. const people:Record<string,any> = {
  3. name: 'chengfeng',
  4. age: 10
  5. }
  6. 复制代码
  • Partial 作用是将传入的属性变为可选项.
  1. interface iPeople {
  2. title: string;
  3. name: string;
  4. }
  5. const people: Partial<iPeople> = {
  6. title: 'Delete inactive users',
  7. };
  8. 定义的结构可以是接口iPeople的任意key
  9. 复制代码
  • Readonly 作用是将传入的属性变为变成只读
  1. interface iPeople {
  2. title: string;
  3. name: string;
  4. }
  5. const people: Readonly<Todo> = {
  6. title: 'todo list',
  7. name: chenfeng;
  8. };
  9. title name属性就是只读的了
  10. 复制代码
  • Required 的作用是将传入的属性变为必选项
  1. interface iPeople {
  2. title?: string;
  3. name?: string;
  4. }
  5. const people1: Props = { title: 'ts' }; // OK
  6. const people22: Required<iPeople> = { title: 'ts' }; // Error: property 'name' missing

查看更多

14. ts一些好用的小tips

  • keyof
  1. interface iPeople {
  2. name: string;
  3. age: number
  4. }
  5. type T = keyof iPeople // -> "name" | "age"
  • in
  1. type Keys = "a" | "b"
  2. type Obj = {
  3. [p in Keys]: any
  4. } // -> { a: any, b: any }

15. 仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state,

  1. // bad
  2. constructor (){
  3. this.setState({ people: this.props.people })
  4. }
  5. // good
  6. state: IState = {
  7. people: {},
  8. };

16. 渲染默认值

  • 添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
  1. // bad
  2. render(){
  3. {name}
  4. }
  5. // good
  6. render(){
  7. {!!name || '--'}
  8. }
  9. 复制代码
  • 还有一种情况,就是本来后端应该返回一个数组给你,但是数据库取不到数据,可能后端给你返回了null,然后前端null.length。这样就gg了
  1. // bad
  2. const { list, totalCount } = await getPeopleList(keyword, page, pageSize);
  3. list 可能是null或者undefined
  4. list.length将直接导致前端报错
  5. this.setState({
  6. status: STATUS.READY,
  7. apps: list,
  8. total: totalCount,
  9. page: page,
  10. });
  11. // good
  12. const { list, totalCount } = await getPeopleList(keyword, page, pageSize);
  13. this.setState({
  14. status: STATUS.READY,
  15. apps: list || [],
  16. total: totalCount || 0,
  17. page: page,
  18. });

17. 不确定的属性,最后却疯狂的用…访问不存在的属性

例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接data.dataList.forEach()

  1. // bad
  2. const data = await getPeopleList(keyword, page, pageSize);
  3. data.dataList.forEach() // 直接挂了
  4. // good
  5. const data = await getPeopleList(keyword, page, pageSize);
  6. if (data && data.dataList && Array.isArray(data.dataList) {
  7. data.dataList.forEach()
  8. }

18. React简单组件可以使用函数代替

  1. // bad
  2. class Listing extends React.Component {
  3. render() {
  4. return <div>{this.props.hello}</div>;
  5. }
  6. }
  7. // good
  8. function Listing({ hello }) {
  9. return <div>{hello}</div>;
  10. }

19. 对于常用的属性进行缓存

  1. // bad
  2. this.props.app.openid;
  3. this.state.time
  4. // good
  5. const { app } = this.props;
  6. const { time } = this.state;
  7. console.log(app.openid)

20. input 输入框使用 trim()

  1. // bad
  2. let searchContent = form.search.value;
  3. // good
  4. let searchContent = form.search.value.trim();

21. 使用 typescript 与 react-router规范

  • 继承React.Component类的时候,由于自定义的props上没有history 和 match等字段,则需要在自定义props的时候继承RouteComponentProps
  • export component时需要使用withRouter()方法处理
  1. // interface中
  2. import { RouteComponentProps } from 'react-router';
  3. interface IProps extends RouteComponentProps {
  4. // id: number;
  5. }
  6. // tsx中
  7. import { withRouter, RouteComponentProps } from 'react-router-dom';
  8. class App extends React.Component<IProps & IState> {
  9. }
  10. export default withRouter(connect(mapStateToProps, mapDispatchToProps)(EmployeeListComponent));

22. 组件嵌套过深

  • 组件一般不要超过三层,最多四层,层级过深可能会导致数据传递过深,在做一些颗粒度比较细的操作的时候,处理起来较为繁琐,可以使用 redux 等状态管理工具替代。

23. 代码过滤掉你没考虑到的情况

  • 例如一个函数,你只想操作字符串,那你必须在函数开头就只允许参数是字符串
  1. function parse (str:string){
  2. if (typeof(str) === 'string' ) {
  3. }
  4. }

24. setState有三种用法

  1. // 对象
  2. this.setState({
  3. })
  4. // 函数,一般是用于在setState之前做一些操作
  5. this.setState(
  6. () => {
  7. // TODO
  8. console.log('')
  9. return {
  10. a:300
  11. }
  12. }
  13. )
  14. // 第二个参数,一般是用于在setState之后做一些操作
  15. this.setState({
  16. a:300
  17. }, () => {
  18. // TODO
  19. })

25. 在 componentWillUnmount 里面去除副作用的函数

  • 清除 EventListener
  • 中止数据请求
  • 清除定时器

26. 循环时key的使用

  • 对于组件中的 key 优化,起到最大化重用 dom
  1. //bad
  2. this.state.dataAry.map((item, index) => {
  3. return <span key={index} />;
  4. });
  5. //good
  6. this.state.dataAry.map(item => <span key={item.id} />);

27. 在组件中获取真实 dom

  • 使用 16 版本后的 createRef()函数
  1. class MyComponent extends React.Component<iProps, iState> {
  2. constructor(props) {
  3. super(props);
  4. this.inputRef = React.createRef();
  5. }
  6. render() {
  7. return <input type="text" ref={this.inputRef} />;
  8. }
  9. componentDidMount() {
  10. this.inputRef.current.focus();
  11. }
  12. }

28. 减少魔法数字

  • 写代码的时候尽量减少一些未知含义的数字,尽量用英文单词。例如type === 0的时候做了一些操作,让人不知所以然。
  1. // bad
  2. if (type !== 0) {
  3. // TODO
  4. }
  5. // good
  6. const STATUS: Record<string, any> = {
  7. READY: 0,
  8. FETCHING: 1,
  9. FAILED: 2
  10. };
  11. if (type === STATUS.READY) {
  12. // TODO
  13. }
  14. // best
  15. enum STATUS {
  16. // 就绪
  17. READY = 0,
  18. // 请求中
  19. FETCHING = 1,
  20. // 请求失败
  21. FAILED = 2,
  22. }

29. 不要使用renderXXX,要使用函数式组件

发现团队一些小伙伴为了减少render函数里面的代码量,会把一些元素拆分到函数里面。

  1. // bad
  2. renderHeader = () => {
  3. return (<div />)
  4. }
  5. renderBody = () => {
  6. return (<div />)
  7. }
  8. renderFooter = () => {
  9. return (<div />)
  10. }
  11. render(){
  12. return(
  13. <div>
  14. renderHeader()
  15. renderBody()
  16. renderFooter()
  17. </div>
  18. )
  19. }

更好的办法,是用函数式组件取代在当前组件里面写方法

  1. // good
  2. function RenderHeader(props) = {
  3. return (<div />)
  4. }
  5. function RenderBody(props) = {
  6. return (<div />)
  7. }
  8. function RenderFooter(props) = {
  9. return (<div />)
  10. }
  11. class Component extends React.Component<iProps, iState>{
  12. render () {
  13. return(
  14. <div>
  15. <RenderHeader />
  16. <RenderBody />
  17. <RenderFooter />
  18. </div>
  19. )
  20. }
  21. }

30. void 0 替代undefined

  1. clearSessioin = () => {
  2. req.session.userName = undefined;
  3. req.session.userName = void 0
  4. }

31. 代码整洁

  • 未使用到的方法和import的文件,上传版本之前要删除

32. interface 定义规范

  • interface应该定义在对应的namespace中, namespace命名 N+模块名(可多个)+功能名+namespaces 如:NLoginInfoNamespaces
  • interface 接口请求参数定义 I+模块名(可多个)+功能名+Params 如:IUserInfoParams