注释
文件顶部的注释,包括描述、作者、日期
1 | /** |
模块的注释
1 | /** |
业务代码注释
1 | /*业务代码注释*/ |
变量注释
1 | interface IState { |
引用组件顺序
- 先引用外部组件库,,再引用当前组件块级组件, 然后是 common 里的公共函数库最后是 css 样式
1 | import * as React from 'react'; |
引号
- 使用单引号,或者 es6 的反引号
缩进
- 使用两个空格
1 | const handleCheck = () => { |
分号
- 每行末尾都要加分号。
括号
下列关键字后必须有大括号(即使代码块的内容只有一行):if
, else
, for
, while
, do
, switch
, try
, catch
, finally
, with
。1
2
3
4
5
6
7// not good
if (condition) doSomething();
// good
if (condition) {
doSomething();
}
空格
- 二元和三元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
1 | // bad |
- 用作代码块起始的左花括号 { 前必须有一个空格。
1 | // bad |
if
/else
/for
/while
/function
/switch
/do
/try
/catch
/finally
关键字后,必须有一个空格。
1 | // bad |
- 在对象创建时,属性中的
:
之后必须有空格,:
之前不允许有空格。
1 | // bad |
换行
- 每个独立语句结束后必须换行。
- 在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
1 | // bad |
- 下列关键字后:else, catch, finally 不需要换行
1 | // bad |
数组、对象
- 对象属性名不需要加引号;
- 对象以缩进的形式书写,不要写在一行;
- 数组最后不要有逗号。
- 对象最后要有逗号。
1 | // bad |
命名
- 类名: 大驼峰式风格,字母和数字,例如:AbcTest。禁止汉字、特殊符号,禁止非大驼峰式风格。
- 函数名: 小驼峰式风格,字母和数字,例如:abcTest。禁止汉字、特殊符号,禁止非小驼峰式风格,例如snake_case等。
- 变量名: 同函数名。
- 常量: 全大写风格,大写字母、数字和下划线,单词之间以下划线分隔,例如:ABC_TEST。禁止汉字、特殊符号、小写字母。
- 使用 onXxx 形式作为 props 中用于回调的属性名称。
1 | interface IProps { |
- 组件内的事件函数使用 handle 开头尾,handleCheckBtn。
- 使用 withXxx 形式的词作为高阶组件的名称。
- 接口命名前面带上 I 表示 interface
1 | interface IProps {} |
类型断言
1 | // bad |
interface声明顺序
日常用到比较多的是四种,只读参数放第一位,必选参数第二位,可选参数次之,不确定参数放最后1
2
3
4
5
6
7
8interface iProps {
readonly x: number;
readonly y: number;
name: string;
age: number;
height?: number;
[propName: string]: any;
}
ts好用的相关工具泛型
Record<string,any>
用这个来声明对象结构的类型
1 | // 用于定义一个javascript的对象,key是字符串,value是任意类型 |
Partial
作用是将传入的属性变为可选项.
1 | interface iPeople { |
Readonly
作用是将传入的属性变为变成只读
1 | interface iPeople { |
Required
的作用是将传入的属性变为必选项
1 | interface iPeople { |
ts一些好用的小tips
keyof
1 | interface iPeople { |
in
1 | type Keys = "a" | "b" |
规范其他
- 不要使用 var 声明变量
- 不会被修改的变量使用 const 声明
- 去除声明但未被引用的代码
- 禁止在代码里使用 debug
- 不允许有空的代码块
仅当初始 state 需要从 props 计算得到的时候,才将 state 的声明放在构造函数中,其它情况下使用静态属性声明 state,并且一般情况下不要将 prop 传给 state,
1 | // bad |
渲染默认值
- 添加非空判断可以提高代码的稳健性,例如后端返回的一些值,可能会出现不存在的情况,应该要给默认值.
1 | // bad |
- 还有一种情况,就是本来后端应该返回一个数组给你,但是数据库取不到数据,可能后端给你返回了null,然后前端null.length。这样就gg了
1 | // bad |
不确定的属性,最后却疯狂的用…访问不存在的属性
例如一些地方,不确定这个变量里面到底有什么,但自己觉得有,就疯狂的…,最明显的就是后端返回了一个对象给你,前端拿到之后判断都不判断直接 data.dataList.forEach()
1
2
3
4
5
6
7
8
9// 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()
}
数据格式转换
把字符串转整型可以使用 +
号
1 | let maxPrice = +form.maxPrice.value; |
转成 boolean
值用 !!
1 | let mobile = !!ua.match(/iPhone|iPad|Android|iPod|Windows Phone/); |
判断条件真假
js 中以下为假,其他情况为真
- false
- null
- undefined
- 0
- ‘’ (空字符串)
- NaN
简单组件可以使用函数代替
1 | // bad |
对于常用的属性进行缓存
1 | // bad |
input 输入框使用 trim()
1 | // bad |
使用 location 跳转前需要先转义
1 | // bad |
同时开发,数据请求 api 目录 git 冲突目录方案
在 api 目录下新建一个目录,目录对应一级 tab,这个目录内放置一个 index.js ,最后把二级 tab 组件所使用的 api 请求都在这个 index.js 内引入。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 目前
|- api
|- pageA.ts
|- pageB.ts
// 建议
|- api
|- pageA
|- index.js
|- aaa.js
|- bbb.js
|- pageB
|- index.js
|- aaa.js
|- bbb.js
|- ccc.js
组件嵌套过深
组件一般不要超过三层,最多四层,层级过深可能会导致数据传递过深,在做一些颗粒度比较细的操作的时候,处理起来较为繁琐,可以使用 redux 等状态管理工具替代。
代码过滤掉你没考虑到的情况
例如一个函数,你只想操作字符串,那你必须在函数开头就只允许参数是字符串
1 | function parse (str:string){ |
业务代码里面的异步请求需要 try catch
ajax 请求,使用 try catch,错误提示后端返回,并且做一些失败后的状态操作例如进入列表页,我们需要一个 loading 状态,然后去请求数据,可是失败之后,也需要把 loading 状态去掉,把 loading 隐藏的代码就写在 finally 里面。
1 | getStudentList = async () => { |
setState有三种用法
1 | // 对象 |
setState可能是同步的
- setState 在react里的合成事件和钩子函数中是“异步”的。
- setState 在原生事件和 setTimeout 中是同步的。
不要在 setState 前面加 await
setState 前面也是可以带 await 的,会变成同步设置状态,但这是一种巧合,不确定未来哪个版本就不支持了,为了遵循 react 框架的设计原则,我们使用回掉函数的形式。
1 | // bad |
阻止事件默认行为
在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault 。
在 componentWillUnmount 里面去除副作用的函数
- 清除 EventListener
- 中止数据请求
- 清除定时器
key
对于组件中的 key 优化,起到最大化重用 dom
1 | //bad |
for-in 中一定要有 hasOwnProperty 的判断(即禁止直接读取原型对象的属性)
1 | //bad |
第三方库函数的使用
用 try catch 包裹,防止第三方库的出现错误,导致整个程序崩溃
1 | /* |
防止 xss 攻击
input,textarea 等标签,不要直接把 html 文本直接渲染在页面上,使用 xssb 等过滤之后再输出到标签上;
1 | import { html2text } from 'xss'; |
在组件中获取真实 dom
使用 16 版本后的 createRef()函数
1 | class MyComponent extends React.Component<iProps, iState> { |
减少魔法数字
写代码的时候尽量减少一些未知含义的数字,尽量用英文单词。例如type === 0的时候做了一些操作,让人不知所以然。
1 | // bad |
如果需要优化 react 性能(一般用不到)
如果组件的 state 和 props 都是简单类型,可以继承 PureComponent 而不是 Component
1 | import { Component, PureComponent } from 'react'; |
Event 事件对象类型
很多小伙伴用了很久的ts,都不知道常用 Event 事件对象类型:
ClipboardEvent
DragEvent
ChangeEvent
KeyboardEvent
MouseEvent
TouchEvent
WheelEvent
AnimationEvent
TransitionEvent
1 | import { MouseEvent } from 'react'; |
使用私有属性取代state状态
对于一些不需要控制ui的状态属性,我们可以直接绑到this上, 即私有属性,没有必要弄到this.state上,不然会触发渲染机制,造成性能浪费 例如请求翻页数据的时候,我们都会有个变量。
1 | // bad |
代码细粒度的思考
总结四句话。我们在写组件或者函数的的时候,工具函数和业务逻辑抽离,表单校验和业务抽离、事件函数和业务抽离,ajax和业务抽离。
例如有些页面是通过location.href跳转的,我们有些业务逻辑等都是放到didmountMount,但是后期改需求,可能要用react-router进行跳转,可能要改的逻辑就会很多了,所以函数抽离出来,需求更新就少改一点代码。
如果还不确定如何划分函数的细粒度,我有个建议。使用过两次以上的代码,要抽离组件或者函数,两次的可以不用
if else 等判断太多了,后期难以维护。
个人觉得if else 嵌套深看起来也不会太难受,难受的是,项目迭代久之后,自己都忘记曾经写过这些代码,而且类型多或者不确定有什么类型,是否后期还会加的情况下,改起来就非常复杂了,而且很容易踩坑和背锅。
用配置取代if嵌套,大概就是抽离一个config.ts出来,里面放一些配置。
1 | 例如你的业务代码里面,会根据不同url参数,代码会执行不同的逻辑. |
不要使用renderXXX,要使用函数式组件
发现团队一些小伙伴为了减少render函数里面的代码量,会把一些元素拆分到函数里面。
1 | // bad |
更好的办法,是用函数式组件取代在当前组件里面写方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 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>
)
}
}
a标签安全问题
使用a标签打开一个新窗口过程中的安全问题。新页面中可以使用window.opener来控制原始页面。如果新老页面同域,那么在新页面中可以任意操作原始页面。如果是不同域,新页面中依然可以通过window.opener.location,访问到原始页面的location对象
在带有target=”_blank”的a标签中,加上rel=”noopener”属性。如果使用window.open的方式打开页面,将opener对象置为空。
1 | var newWindow = window.open(); |
void 0 替代undefined
1 | clearSessioin = () => { |
前端不要操作cookie
在做一些前后端鉴权的时候,后端应该开启domain,secure,httponly严格模式,禁止前端操作cookie,防止csrf攻击。