起源
想写一下关于前端防御性编程的内容,这个内容好像在前端并没有提及特别多。
但是从个人角度来看,这个应该得到重视,但是这些往往是在开发中的细节,开发中很容易忽视掉。同时因为 JavaScript
语言的原因,缺失了传统面向对象的编译期,这些问题就更不容易发现了。
我觉得关于前端防御性编程,应该可以做成一个规范,现在这个事情多数时候是 Lint
工具帮我们做的,或者说是一些关于应用健壮性的思考,后续如果有机会想思考一下规范的事情。
前言
这篇文章想通过个人视角,从 React
出发,来谈谈开发过程中,需要注意的一些小点,管中窥豹。
熟悉流程
我们先从页面渲染,或者说在提前一些,从输入 URL
开始说。
- 输入
URL
,向服务器请求资源。 - 加载请求到的资源,渲染页面。
- 进入主页面,进行交互。
输入URL请求资源
看起来这个地方没什么好说的(也确实没啥好说的),就是简单的输入 URL
然后浏览器去解析,似乎并没有什么前端可以做的。
前端一般做好 404
页面即可,选择提示输入错误或者直接重定向到主页。
<Route path="*">
<NoMatch />
</Route>
加载资源、渲染页面
这个地方也看似没有什么好做的,但其实不然,考虑的地方还是有些的。
- 缓存
比较重要的一个点在于缓存,我们是否采用本地缓存来加载,这个点需要持续关注一下。包括但不限于缓存策略、过期时间。
- 无网络(弱网)
无网络的情况下,策略是什么。以及弱网情况下,策略是什么,等待加载 or
交互提示。
这个地方涉及的挺广泛的,PC端、移动端策略都不太一样。如果是在移动端的策略上,如果要提高用户体验,弱网情况下,也应该加载本地组件(离线包)。有一些最基础的交互,并应该给出提示。
页面交互
页面交互其实也差不多,在处理大型输入输出、网络请求的时候,是否有完好的组件 Loading
状态,是否有比较完备的提示内容。
深入代码
1. 无网络提示(弱网)
这个地方策略不尽相同,全局鉴权提示网络不畅通、弹窗提示并跳转至某个页面等等。
我综合了团队中一些人的意见,意见比较一致的是全局鉴权提示。
我们可以对所有 API
进行请求拦截,如果请求网络错误,直接通过 errorMessage
或是一些提示组件进行提示。
export const request: RequestConfig = {
credentials: 'include',
errorConfig: {
adaptor: (resData) => {
// 全局adapter,或者通过拦截器
},
},
};
2. 组件初始(初始化、赋值)
这其实是一个比较难讲的事情,初始化的时候,该传哪些值?哪些值可选,哪些不可选?这些其实是一个编码时候的提示,这个使用 TypeScript
就可以。
export default (props: {
// 用于页面刷新的回调
pageRefresh?: () => void;
// 用于判定范围,请求的范围参数(不传默认为范围一)
inventoryScopeNo?: string;
// 用于判断范围下的是哪个排放范围
categoryNo?: string;
}) => {
return
// 省略了组件
}
但是这还不够,除了编码提示,我们也不能完完全全把这个问题交给 TypeScript
来做,我们也应该自己做好判断。
假设上方的组件,在调用的时候 props
并没有给其传 categoryNo
值,我们又要采用,我们就应该给其赋初值。
const { pageRefresh, categoryNo = "01", inventoryScopeNo = "001" } = props;
或者说如下:
const { pageRefresh, categoryNo, inventoryScopeNo} = props || {
pageRefresh: ()=>void,
categoryNo: "01",
inventoryScopeNo: "001",
};
这样我们可以预判一些特殊的情况,从而让程序按照我们的想法来做。
3. 网络交互
除了做好组件的初始化,赋值工作外,更多时候,我们用某个组件都是要将数据放置到这个组件上去,这个时候,不仅仅在组件方面做好特殊情况的处理,在网络请求获取方面,也要做这样一个事情。
const { data ={ list: [] }, success } =
await services.EmissonDataEntryController.queryDataEntryItemsList({
dataEntryItemQueryRequest: {
current,
pageSize,
inventoryScopeNo: INVENTORY_SCOPE_NO_RANGE_ONE,
emissionCategoryNo: categoryNo,
},
});
return {
data: data?.list,
success,
};
思考
但是这个地方,团队另外一个伙伴@牧亿 ,他给了我另外一个思考和想法,前端是否需要做这方面的内容。
某种意义上上,像是赋初值、出错的时候,应该展示的数据。这些真的应该展示么?
假设接口方面报错了,然后前端给了默认值,让用户觉得:哦,没有错哦,你看没有报错嘛!是不是不太好?这种问题,是否就是应该直接抛错误,或者说就是通过 errorMsg
这种报错弹窗弹出来,这也是一个值得思考的问题。
短期来看,我暂时无法判断出两者的好坏,我觉得两者都是可以接受的,后续会持续关注这个问题。
总结
不管怎么说,防御性编程的思想,一定要在开发中的点滴,要深入开发中的各种细节部分。
只有做好了这一块的内容,后续的开发才会更加顺利。