- 01 类型声明
- 02 注释
- 03. 引用组件顺序
- 04. 引号
- 05. 缩进
- 07. 括号
- 08. 空格
- 8. 换行
- 9. 数组、对象
- 10. 命名
- 11. 类型断言
- 12. interface声明顺序
- 14. ts一些好用的小tips
- 15. 规范其他
- 16. State & Props
- 17. 渲染默认值
- 18. 不确定的属性,最后却疯狂的用…访问不存在的属性
- 19. 数据格式转换
- 20. 判断条件真假
- 21. 简单组件可以使用函数代替
- 22. 对于常用的属性进行缓存
- 23. input 输入框使用 trim()
- 24. 使用 location 跳转前需要先转义
- 25. 使用 react-router
- 26. 同时开发,数据请求 api 目录 git 冲突目录方案
- 27. 业务代码里面的异步请求需要 try catch
- 28. setState有三种用法
- 29. setState可能是同步的
- 30. 不要在 setState 前面加 await
- 31. 阻止事件默认行为
- 32. 在 componentWillUnmount 里面去除副作用的函数
- 33. key
- 34. for-in 中一定要有 hasOwnProperty 的判断(即禁止直接读取原型对象的属性)
- 35. 第三方库函数的使用
- 36. 在组件中获取真实 dom
- 37. 减少魔法数字
- 38. Event 事件对象类型
- 39. 使用私有属性取代state状态
- 50. 前端不要操作cookie
01 类型声明
// 先定义类型
interface Basic {
num: number;
str: string | null;
bol?: boolean;
}
interface Func {
func(str: string): void;
}
interface Arr {
str: string[];
mixed: Array<string | number>;
fixedStructure: [string, number];
basics: Basic[];
}
// on(type: string, cb: Function): {}
event.on('change', function() {});
type Cb = () => void;
on(type: string, cb: Cb);
// 枚举类型
enum Status {
Draft,
Published
}
// 也可指定值
enum Status {
Draft = 'Draft',
Published = 'Published'
}
(1) 基本类型
// string
let employeeName:string = 'John Smith';
// boolean
let isPresent:boolean = true
// Array
let fruits: string[] = ['Apple', 'Orange', 'Banana']
// tuple
let user: [number, string, boolean, number, string];// declare tuple variable
let user = [1, "Steve", true, 20, "Admin"];// initialize tuple variable
// Enum
enum PrintMedia {
Newspaper,
Newsletter,
Magazine,
Book
}
// Union
let code: (string | number);
code = 123; // OK
code = "ABC"; // OK
code = false; // Compiler Error
// any
let something: any = "Hello World!";
something = 23;
something = true;
// void
function sayHi():void {
console.log('Hi!')
}
let speech: void = sayHi();
console.log(speech); // Output: undefined
// Never
function throwError(errorMsg: string): never {
throw new Error(errorMsg);
}
function keepProcessing(): never {
while (true) {
console.log('I always does something and never ends.')
}
}
(2) 申明方式
Record
用这个来声明对象结构的类型
用于定义一个javascript的对象,key是字符串,value是任意类型 const people:Record= { name: ‘chengfeng’, age: 10 } Partial 作用是将传入的属性变为可选项. ```typescript interface iPeople { title: string; name: string; }
const people: Partial
- **keyof 可以用来取得一个对象接口的所有 key 值.**
```typescript
interface Foo {
name: string;
age: number
}
// T -> "name" | "age"
type T = keyof Foo
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
}
// Obj -> { a: any, b: any }
- Readonly 作用是将传入的属性变为变成只读 ```typescript interface iPeople { title: string; name: string; }
const people: Readonly
- **Required 的作用是将传入的属性变为必选项 **
```typescript
type Required<T> = { [P in keyof T]-?: T[P] };
interface iPeople {
title?: string;
name?: string;
}
type Requiredmoe = Required<iPeople>
const people1: iPeople = { title: 'ts' }; // OK
const people22: Requiredmoe = { title: 'ts' }; // Error: property 'name' missing
- PICK 从 T 中取出 一系列 K 的属性 【 解构 】
```typescript
type Pick
= { [P in K]: T[P] };
interface User { age: number; name: string; keydirction:string; };
// 相当于: type PickUser = { age: number; name: string; }
type PickUser = Pick
- **Exclude 前面类型T 的与后面类型U 对比,过滤出前面T 独有的属性 [交集+ 减集]**
```typescript
T extends U ? X : Y // 如果 T 是 U 的子类型的话,那么就会返回 X,否则返回 Y
type Exclude<T, U> = T extends U ? never : T;
const str: Exclude<'a' | '1' | '2', 'a' | 'y' | 'z'> = '1';
- Omit Pick & Exclude 去除掉 接口内的 某属性。 【减集】
```typescript
// Pick
type Pick
= { [P in K]: T[P] };
// Exclude
type Exclude
// Omit
type Omit = Pick
type Omit
interface User { id: number; age: number; name: string; }
type OmitUser = Omit
```typescript
// 首先声明一下模块:
declare module 'progressbar.js' {
// 模块中暴露了 Circle 类
export class Circle {
constructor(container: HTMLElement, options: Options);
}
// 构造函数的 Options 需要单独声明
interface Options {
easing?: string;
strokeWidth?: number;
trailColor?: string;
trailWidth?: number;
}
}
02 注释
(1) 文件顶部的注释,包括描述、作者、日期
/**
* @description xxxxxx
* @author wuliang
* @since 20/05/20
*/
(2) 模块的注释
/**
* 拷贝数据
* @param {*} data 要拷贝的源数据
* @param {boolean} [isDeep=false] 是否深拷贝,默认浅拷贝
* @return {*} 返回拷贝后的数据
*/
(4) 变量注释
interface IState {
// 名字
name: string;
// 电话
phone: number;
// 地址
address: string;
}
03. 引用组件顺序
- 先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 css 样式
import * as React from 'react';
import { Dropdown, Menu, Icon } from 'antd';
import Header from './Header';
import toast from 'common/toast';
import './index.less';
04. 引号
- 使用单引号,或者 es6 的反引号
05. 缩进
- 使用两个空格
const handleCheck = () => { onCancel && onCancel(); onClose && onClose(); - 除了代码块的以外的每个表达式后必须加分号。
07. 括号
下列关键字后必须有大括号(即使代码块的内容只有一行):if, else, for, while, do, switch, try, catch, finally, with。
// not good
if (condition) doSomething();
// good
if (condition) {
doSomething();
}
08. 空格
二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
// 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。禁止汉字、特殊符号,禁止非大驼峰式风格。
函数名: 小驼峰式风格,字母和数字,例如:abcTest。禁止汉字、特殊符号,禁止非小驼峰式风格,例如snake_case等。
变量名: 同函数名。
常量: 全大写风格,大写字母、数字和下划线,单词之间以下划线分隔,例如:ABC_TEST。禁止汉字、特殊符号、小写字母。
使用 onXxx 形式作为 props 中用于回调的属性名称。
interface IProps { onClose?: () => void; onOk?: (item: Record) => 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;
}
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. 规范其他
- 不要使用 var 声明变量
- 不会被修改的变量使用 const 声明
- 去除声明但未被引用的代码
- 禁止在代码里使用 debug
- 不允许有空的代码块
16. State & Props
仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state
// bad
constructor (){
this.setState({ people: this.props.people })
}
// good
state: IState = {
people: {},
};
17. 渲染默认值
添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
// 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, });
18. 不确定的属性,最后却疯狂的用…访问不存在的属性
例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接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()
}
19. 数据格式转换
- 把字符串转整型可以使用+号
let maxPrice = +form.maxPrice.value; let maxPrice = Number(form.maxPrice.value); - 转成 boolean 值用!!
let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/);
20. 判断条件真假
js 中以下为假,其他情况为真
- false
- null
- undefined
- 0
- ‘’ (空字符串)
- NaN
21. 简单组件可以使用函数代替
// bad
class Listing extends React.Component {
render() {
return <div>{this.props.hello}</div>;
}
}
// good
function Listing({ hello }) {
return <div>{hello}</div>;
}
22. 对于常用的属性进行缓存
// bad
this.props.app.openid;
this.state.time
// good
const { app } = this.props;
const { time } = this.state;
console.log(app.openid)
23. input 输入框使用 trim()
// bad
let searchContent = form.search.value;
// good
let searchContent = form.search.value.trim();
24. 使用 location 跳转前需要先转义
// bad
window.location.href = redirectUrl + '?a=10&b=20';
// good
window.location.href = redirectUrl + encodeURIComponent('?a=10&b=20');
25. 使用 react-router
// bad
import { withRouter, RouteComponentProps } from 'react-router-dom';
export interface IProps extends RouteComponentProps<any> {}
class App extends React.Component<IProps, AppStates> {}
export default withRouter(App);
// good
import { withRouter, RouteComponentProps } from 'react-router-dom';
class App extends React.Component<IProps & RouteComponentProps<{}>, AppStates> {}
export default withRouter(App);
26. 同时开发,数据请求 api 目录 git 冲突目录方案
- 在 api 目录下新建一个目录,目录对应一级 tab,这个目录内放置一个 index.js ,最后把二级 tab 组件所使用的 api 请求都在这个 index.js 内引入。
// 目前
|- api |- pageA.ts |- pageB.ts
// 建议
|- api |- pageA |- index.js |- aaa.js |- bbb.js |- pageB |- index.js |- aaa.js |- bbb.js |- ccc.js
27. 业务代码里面的异步请求需要 try catch
ajax 请求,使用 try catch,错误提示后端返回,并且做一些失败后的状态操作例如进入列表页,我们需要一个 loading 状态,然后去请求数据,可是失败之后,也需要把 loading 状态去掉,把 loading 隐藏的代码就写在 finally 里面。
getStudentList = async () => {
try {
this.setState({
loading: true,
isEmpty: false
});
await getStudentList({});
}
catch (e) {
// TODO
console.log(e)
}
finally {
// 失败之后的一些兜底操作
this.setState({
loading: false,
isEmpty: true
});
}
};
28. setState有三种用法
// 对象
this.setState({
})
// 函数,一般是用于在setState之前做一些操作
this.setState(
() => {
// TODO
console.log('')
return {
a:300
}
}
)
// 第二个参数,一般是用于在setState之后做一些操作
this.setState({
a:300
}, () => {
// TODO
})
29. setState可能是同步的
- setState 在react里的合成事件和钩子函数中是“异步”的。
- setState 在原生事件和 setTimeout 中是同步的。
30. 不要在 setState 前面加 await
- setState 前面也是可以带 await 的,会变成同步设置状态,但这是一种巧合,不确定未来哪个版本就不支持了,为了遵循 react 框架的设计原则,我们使用回掉函数的形式。
// bad func = async (name, value, status) => { await this.setState({ name }); // TODO };- 建议这种写法
// good func = (name, value, status) => { this.setState( { name }, () => { // TODO } ); };
31. 阻止事件默认行为
- 在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault 。
32. 在 componentWillUnmount 里面去除副作用的函数
- 清除 EventListener
- 中止数据请求
- 清除定时器
33. key
- 对于组件中的 key 优化,起到最大化重用 dom
//bad this.state.dataAry.map((item, index) => { return ; });
//good this.state.dataAry.map(item => );
34. for-in 中一定要有 hasOwnProperty 的判断(即禁止直接读取原型对象的属性)
//bad
const arr = [];
const key = '';
for (key in obj) {
arr.push(obj[key]);
}
//good
const arr = [];
const key = '';
for (key in obj) {
if (obj.hasOwnProperty(key)) {
arr.push(obj[key]);
}
}
35. 第三方库函数的使用
用 try catch 包裹,防止第三方库的出现错误,导致整个程序崩溃
/*
- Echart 用于代绘制图表,但当其自身发生错误时,可能影响到业务代码的执行
*/ // bad const iniDom = document.getElementById(‘init-container’); const echartObj = echarts.init(iniDom); this.setState( { echartObj }, () => { const { echartObj } = this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); } );
// good try { const iniDom = document.getElementById(‘init-container’); const echartObj = echarts.init(iniDom); this.setState( { echartObj }, () => { const { echartObj } = this.state; // 更新图表 echartObj.setOption(CHART_CONFIG, true); } ); } catch (error) { // TODO }
36. 在组件中获取真实 dom
- 使用 16 版本后的 createRef()函数
class MyComponent extends React.Component{ constructor(props) { super(props); this.inputRef = React.createRef(); }
render() { return ; }
componentDidMount() { this.inputRef.current.focus(); } }
37. 减少魔法数字
- 写代码的时候尽量减少一些未知含义的数字,尽量用英文单词。例如type === 0的时候做了一些操作,让人不知所以然。
// bad if (type !== 0) { // TODO }
// good const STATUS: Record= { READY: 0, FETCHING: 1, FAILED: 2 };
if (type === STATUS.READY) { // TODO }
// best enum STATUS { // 就绪 READY = 0, // 请求中 FETCHING = 1, // 请求失败 FAILED = 2, }
38. Event 事件对象类型
很多小伙伴用了很久的ts,都不知道常用 Event 事件对象类型:
ClipboardEvent
DragEvent
ChangeEvent
KeyboardEvent
MouseEvent
TouchEvent
WheelEvent
AnimationEvent
TransitionEvent
import { MouseEvent } from 'react';
interface IProps {
onClick(event: MouseEvent<HTMLDivElement>): void;
}
39. 使用私有属性取代state状态
对于一些不需要控制ui的状态属性,我们可以直接绑到this上, 即私有属性,没有必要弄到this.state上,不然会触发渲染机制,造成性能浪费 例如请求翻页数据的时候,我们都会有个变量。
50. 前端不要操作cookie
在做一些前后端鉴权的时候,后端应该开启domain,secure,httponly严格模式,禁止前端操作cookie,防止csrf攻击。