(1)业务代码里面的异步请求需要 try catch
ajax 请求,使用 try catch,错误提示后端返回,并且做一些失败后的状态操作例如进入列表页,我们需要一个 loading 状态,然后去请求数据,可是失败之后,也需要把 loading 状态去掉,把 loading 隐藏的代码就写在 finally 里面。
getStudentList = async () => {
try {
this.setState({
loading: true,
isEmpty: false
});
await getStudentList({});
this.setState({
loading: false,
isEmpty: true
});
} catch (e) {
// TODO
console.log(e)
} finally {
// 失败之后的一些兜底操作
this.setState({
loading: false,
isEmpty: true
});
}
};
(2) 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]);
}
}
(3)第三方库函数的使用
用 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
}
(4)防止 xss 攻击
input,textarea 等标签,不要直接把 html 文本直接渲染在页面上,使用 xssb 等过滤之后再输出到标签上;
import { html2text } from 'xss';
render(){
<div
dangerouslySetInnerHTML={{
__html: html2text(htmlContent)
}}
/>
}
(5)在组件中获取真实 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();
}
}
(6)代码细粒度的思考
总结四句话。我们在写组件或者函数的的时候,
- 工具函数和业务逻辑抽离;
- 表单校验和业务抽离;
- 事件函数和业务抽离;
- ajax和业务抽离;
例如有些页面是通过location.href跳转的,我们有些业务逻辑等都是放到didmountMount,但是后期改需求,可能要用react-router进行跳转,可能要改的逻辑就会很多了,所以函数抽离出来,需求更新就少改一点代码。
如果还不确定如何划分函数的细粒度,我有个建议。使用过两次以上的代码,要抽离组件或者函数,两次的可以不用
(7)a标签安全问题
使用a标签打开一个新窗口过程中的安全问题。新页面中可以使用window.opener来控制原始页面。如果新老页面同域,那么在新页面中可以任意操作原始页面。如果是不同域,新页面中依然可以通过window.opener.location,访问到原始页面的location对象
在带有target="_blank"的a标签中,加上rel="noopener"属性。如果使用window.open的方式打开页面,将opener对象置为空。
var newWindow = window.open();
newWindow.opener = null;