一个可以提升用户体验和交互效果的模块 InteractionMnager(交互管理器)
一、基本内容
使用 InteractionManager 可以让一些耗时的任务在交互操作或者动画完成之后进行执行,这样使用可以保证我们的 JavaScript 的动画效果可以平滑流畅的执行。可以大大提升用户体验。比如:导航的转场动画
对大多数 React Native 应用来说,业务逻辑是运行在 JavaScript 线程上的。这是 React 应用所在的线程,也是发生 API 调用,以及处理触摸事件等操作的线程。更新数据到原生支持的视图是批量进行的,并且在事件循环每进行一次的时候被发送到原生端,这一步通常会在一帧时间结束之前处理完(如果一切顺利的话)。如果 JavaScript 线程有一帧没有及时响应,就被认为发生了一次丢帧。例如,你在一个复杂应用的根组件上调用了 this.setState,从而导致一次开销很大的子组件树的重绘,可想而知,这可能会花费 200ms 也就是整整 12 帧的丢失。此时,任何由 JavaScript 控制的动画都会卡住。只要卡顿超过 100ms,用户就会明显的感觉到。
属性方法
runAfterInteractions(task) 静态方法, 在用户交互和动画结束以后执行任务
createInteractionHandle() 静态方法,创建一个句柄(处理器),通知管理器,某个动画或者交互开始了
clearInteractionHandle(handler:Handle) 静态方法,进行清除句柄,通知管理器,某个动画或者交互结束了。setDeadline(deadline:number) 静态方法, 设置延迟时间,该会调用 setTimeout 方法挂起并且阻塞所有没有完成的任务,然后在 eventLoopRunningTime 到设定的延迟时间后,然后执行 setImmediate 方法进行批量执行任务
Events:CallExpression
addListener:CallExpression
在应用开发中我们可以如下进行执行任务
InteractionManager.runAfterInteractions(() => {// 执行耗时的同步任务});
该模块和其他相关的调度方法对比:
requestAnimationFrame(): 执行控制动画效果的代码
setImmediate/setTimeout(): 设置延迟执行任务的时间,该可能会影响到正在执行的动画
runAfterInteractions(): 延迟执行任务,该不会影响到正在执行的动画效果
触摸系统中的单点或者多点触控都是交互动作,耗时任务会在这些触摸交互动作执行完成之后或者取消以后回调 runAfterInteractions()方法进行执行。
InteractionManager 也允许应用在动画开始的时候通过 createInteractionHandle()方法注册动画生成一个句柄,在结束的时候清除该句柄。
var handle = InteractionManager.createInteractionHandle();
// 执行动画 (`runAfterInteractions` tasks are queued)
// 动画执行结束
InteractionManager.clearInteractionHandle(handle);
// 动画清除之后,开始直接 runAfterInteractions 中的任务
runAfterInteractions 任务也可以接收一个普通的回调函数或者一个带有 gen 方法并且返回一个 Promise 的 PromiseTask 对象。如果参数是 PromiseTask 对象,那么任务是异步执行的,也会阻塞。该会等着当前任务执行完毕以后才能执行下一个任务。
默认情况下,队列任务会一次性在 setImmediate 方法中批量执行。如果你通过 setDeadline 方法设置一个时间值,那么任务会在延迟该设定值时间进行执行。这时候会调用 setTimeout 方法进行挂起任务并且阻塞其他任务的执行。这样可以给触摸交互等操作留出时间更好的相应用户操作
具体使用
InteractionManager.runAfterInteractions(() => {
// 执行耗时的同步任务
navigate("Redeem", { title: "积分兑换"});
})
或者
constructor(props) {super(props);
this.state = {renderPlaceholderOnly: true}
}
render(){if (this.state.renderPlaceholderOnly) {// Toast.loading("加载中...", 1);
return self._renderPlaceholderView();}
return(.....)
}
componentDidMount = () => {InteractionManager.runAfterInteractions(() => {this.setState({ renderPlaceholderOnly: false});
});
};
二、定时器
定时器是一个应用中非常重要的部分。React Native 实现了和浏览器一致的定时器 Timer
提供如下方法
setTimeout, clearTimeout
setInterval, clearInterval
setImmediate, clearImmediate
requestAnimationFrame, cancelAnimationFrame
setTimeout (fn, 1000) 和 setInterval (fn,1000)
和 web 中的意思一样,前者表示延迟 1000 毫秒后执行 fn 方法,后者表示每隔 1000 毫秒执行 fn 方法。
requestAnimationFrame(fn)和 setTimeout(fn, 0)不同,
前者会在每帧刷新之后执行一次,而后者则会尽可能快的执行(在 iPhone5S 上有可能每秒 1000 次以上)。
setImmediate 则会在当前 JavaScript 执行块结束的时候执行,就在将要发送批量响应数据到原生之前。注意如果你在 setImmediate 的回调函数中又执行了 setImmediate,它会紧接着立刻执行,而不会在调用之前等待原生代码。
Promise 的实现就使用了 setImmediate 来执行异步调用。
注意点:
1、定时器功能比较简单,注意在 es6 中使用时,需铭记在 unmount 组件时清除(clearTimeout/clearInterval)所有用到的定时器。
2、可以使用定时器实现一些普通功能:如短信倒计时等
3、对于一些需要延迟执行的特殊场景也可以使用 Timer,譬如:目前 RN 提供的 fetch 是没有提供设置超时时间的,如果客户端请求后端的一个接口,接口超时了(后端服务设置的超时时间为 10s),那么 RN 界面就一直 loading,也不能 aborded。那么这时候我们就可以巧妙的使用计时器,如果客户端发出的 Request,时间大于某个值(5 秒),那么我们直接认为请求失败。
4、今天还发现一个使用 setTimeout 的场景,在列表页加载下一页的时候,如果接口响应很快,就不会出现 loading 的效果,这个时候为了有 loading 的效果,设置一个 500 毫秒的延时