【React】loading 动画 放在请求拦截里做统一处理,还是放在每个页面里
关于loading 动画,目前有两种方案:
1、放在请求拦截里,每次发起请求,都展示loading 动画。
优:这样做的好处是不用每次都写loading 的开与关
2、放在页面里,对每次请求处理(单个或多个请求)做loading 的开与关。
缺:每个页面或请求里都得写loading 的开关逻辑,很繁琐。
以上为个人观点,欢迎大家讨论与补充。
回答
其实就是逻辑统一处理,还是每个页面单独处理的区别。
简单聊聊我所知道的几种不同方案
一、在请求接口时拦截,例如axios的拦截器
这个方案就是题主说的第一种方案。这种方案如果结合vue或者react中使用,那么我们必须要思考的一个问题是,这个loading是代表的那种loading。
很显然,一定得是全局loading,否则很多细节无法处理,例如如果有多个接口同时请求怎么办?因此,要把这种方案实施下来,就需要
- Loading组件独立于所有页面组件,是公用的全局组件
- 多个请求时,需要计算正在请求的个数
如果从代码量考虑,这样的方式确实减少了工作量,但是缺陷也很明显,缺乏灵活性。只要有接口请求,就只能把这个全局Loading抬出来。很多场景无法适应。
当然,如果是后台管理系统,不需要Loading具有太多的灵活性,是可以这样处理的。就看你能不能说服团队其他所有人,这个项目,所有的加载情况都这样处理。
二、在组件中拦截,例如ant design pro中使用的dva-loading
dva-loading是react-redux解决方案中的中间件。dva-loading在全局store中维护了一个EffectLoading的对象,所有loading的状态请求都放在了该对象中,并且每个请求根据namespace的不同都有对应的唯一值。也就是说,这种方式避免了整个全局中有一个loading,而是把所有的loading状态各自有一个唯一的标识能够访问得到。
// EffectLoading{
effects: {
'login/fetch': true,
'login/user': true,
'dashboard/fetch': false,
'dashboard/list': false,
},
global: false,
models: {
'login/fetch': true,
'login/user': true,
}
}
这种方式也是统一处理,但是处理的方式和第一种方式不一样,这种方式更具灵活性,我们可以在组件里针对自己当前组件的状态进行不同的处理。该方式仅仅只是把对应请求的loading传入组件,在组件中如何处理,则自己决定。
这种方式由于第一种方式,当然两种方式的处理思维也可以结合起来,糅合成更合理的方案
但是这样的方案在react中也并非没有缺陷。
dva-loading让整个项目全局共享一个loadingEffect对象。
也就是说,无论你的组件如何划分,loading都不可避免的成为了共享状态。可是在很多情况下,我们仅仅只希望loading成为当前页面或者某个子组件的私有状态。因此dva-loading虽然看上去简化了写法,却让组件化思维固化,失去了灵活性。
也正因为处于最顶层的共享数据,当loadingEffect状态改变时,会导致额外的冗余渲染。因此,dva-loading应该慎用。
三、每个组件,每个请求维护自己的loading状态
当然,这样的方式灵活性都很好,无论交互想要怎么处理,都可以完美解决。缺点也很明显,完美要额外定义很多loading状态,维护成本会稍高,代码也会多些一点。
四、在react中,利用自定义hooks
这是目前我认知到的最完美的方案。
在超性感的的react hooks(五):自定义hooks中我跟大家介绍了如何使用自定义hooks。
并在使用hooks重构antd pro的想象力(三)我是如何利用hooks干掉redux的中介绍了如何自定义一个能够处理请求的hooks。
首先我们定义一个全局的能够处理请求的自定义hooks useInitial
【详细代码见上述文章】
在组件中使用时,只需要如下一句代码即可拿到对应请求的loading状态和请求数据
const {loading, data} = useInitial(api);
该方案是将请求逻辑统一封装成为自定义hooks,极大降低了维护成本,并且每个loading都相互独立,互不影响,灵活性也不存在任何问题。
因此我认为这是最完美的解决方案。不过要基于react hooks。
五、选择最适合自己的方案
结合自己团队和项目的情况,选择最适合自己的方案,才是最好的方案!
并不建议放在拦截器请求里处理loading
1.业务需求有些请求不需要告知客户
2.页面请求数量不固定
3.高类聚低耦合,拦截器里面处理组件业务不怎么合适
对于loading展示具体要看你的业务是否需要给用户提示,例如:提交,删除,添加,获取列表数据,获取详情数据等等,这些是可以给loading的。不需要loading的(例如:一个页面有多个接口调用,只需要给主要展示数据的loading,其余的就不用给了)不建议放在请求拦截里,你这样做了就等于每请求一个接口都会有loading这肯定是不好的。loading的开关其实就是一个方法的事,例如:mint-ui
Indicator.open(); //打开Indicator.close(); //关闭
下面给出 AsyncButton 伪代码实现,其余 loading 组件可类似书写。
class AsyncButton extends React.Componment {
handleClick = async (e, btn) => { btn.loading(true);
try {
await this.props.onClick(e);
} finally {
btn.loading(false);
}
}
render() {
const propsTemp = _.cloneDeep(this.props);
propsTemp.onClick && delete propsTemp.onClick;
return <Button {...propsTemp} onClick={handleClick} />
}
}
首先我们需要搞清楚loading的用处,第一:告诉用户你正在发起一个请求,第二:告诉用户服务请求还没有回来处于等待状态。两则的呈现形式在每个端都有不同的形式:
第一种,我们可以借鉴其他平台的处理方式,比如iOS在所有的请求发起的时候会开启Indicator(单例),等待结束之后然后关闭,他的load是显示在顶部的statusbar,相对于内容来说并没有阻碍到用户体验。
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
如果你也是这样的需求,可以将loading放到请求拦截里,然后loading放置的位置应该不能影响用户的交互。比如下面的右上角
第二种,你可以看到这几年无论是web还是iOS、android的应用中,为了提升应用的加载等待这段时间的用户感知体验,各种奇门遁甲之术层出不穷,像客户端那种hub的形式对用户来说极其不友好已经被很多应用抛弃使用了,其中Skeleton就在这几年流传开来。像此类的需求,处理方式建议可以放到页面层或api层。
- 组件方式
<page :empty="!data" :indicator="show" \>....
</page\>
loading组件或者emptyDataSet组件会在page组件中处理的挂载和卸载,尤其是在pc端,你可以遇到每个section就有一个request的情况,你都需要告诉用户词section正在加载
类似的在客户端中的处理方式
- (LFTableCellItem *)cellItemAtIndexPath:(NSIndexPath *)indexPath { LFTableCellItem *theCellItem = nil;
if ([[self.lfDelegate cellItemsForTableView:self] count] == 0) {
if (_firstLoad) {
self.scrollEnabled = NO;
return self.loadingCellItem;
} else {
self.scrollEnabled = self.allowScrolling;
return self.emptyCellItem;
}
} else {
.....
}
return theCellItem;
}
- 在api层处理的方式
axios.get('/user/12345', { cancelToken: source.token,
showLoading: true
})
然后通过请求拦截层,做相应的处理,这样做的话在分页加载或者下拉刷新的时候showLoading都需要得到响应的控制,成本比较高。
客户端类似的情况可以使用URLProtocol方式来拦截请求,通过标志位来判断是否显示
我认为应该写在拦截器里面。
代码优雅,维护简单,不用每个页面都写。
至于楼下提到的有些请求不需要告知用户,可以在拦截器中配置参数来控制是否显示loading
代码耦合就看自己选择了,每个页面写其实也很繁琐,我个人建议在拦截器中处理
这个问题还可以延伸到请求的错误处理,请求拦截器里做统一处理,还是页面里,单独处理。欢迎讨论
关于loading 需要按照场景区分,此处主要是用户体验的提升,关键点在于,当用户有操作,则需要及时的反馈。
1、首次加载页面还未能获取数据,此时loading,骨架屏都较为合适。
2、如果是列表页面加载,分页切换,此时适合每次请求都有一个loading「在更新数据的地方,而不是全屏的loding」
3、业务相关请求,统计信息,则不需要给用户显示。
4、关于放哪合适,不同场景,不同的操作方式,关键是看code是否优雅。
我的做法是列表查询才在表格里loading,这个通常表格属性里已经自带loading字段了,只需要给这个字段赋值true或false即可。至于其他接口,就算了,因为其他接口基本都是比列表查询快的,那么快,你还需要loading?我认为不需要了。
request.js:
/** * get
* @param {String} url -必选 [地址]
* @param {Object} params -可选 [参数]
* @param { Boolean } isLoading -可选 [是否显示加载状态, 默认渐变显示, 初始为透明, 2s 后显示, 通过 css 控制]
* @return {Object} [Promise]
*/
export const $get = (url, params, isLoading = true) => {}
main.css:
.el-loading-mask.is-fullscreen { background: rgba(255, 255, 255, .9);
transition: opacity .3s 2s;
}
以上是 【React】loading 动画 放在请求拦截里做统一处理,还是放在每个页面里 的全部内容, 来源链接: utcz.com/a/73337.html