RN性能优化及事件监听

自从React Native出世,虽然官方一直尽可能的优化其性能,为了能让其媲美原生App的速度,但是现实感觉有点不尽人意。接下来介绍下实践中遇到的一些性能问题以及优化方案。

一、StackNavigator页面切换动画优化

场景:在navigation还没出来时,导航路由使用NavigatorIOS来实现,页面切换是很流畅的,但是用了StackNavigator navigation发现页面切换会使JS线程出现严重的掉帧(卡顿现象);

原因:
NavigatorIOS的切换动画是跑在UI主线程上,而不是JS线程上的,所以不受JS线程上的掉帧影响;但是NavigatorIOS是但平台专用,在我们业务开发中是不能使用的;而react-navigation是跨平台的,也是官方推荐使用的;

问题:
react-navigation在从A页面push到B页面时,如果B页面render存在大量可操作的视图结构,或者componentDidMount方法里面做了耗时的操作,会发现丢帧的现在,也就是页面页面会卡顿2s左右,从用户体验上来讲是非常不友好的。

react-navigation动画是由JS线程控制的

想象一下“从右边推入”这个场景的切换:每一帧中,新的场景从右向左移动,从屏幕右边缘开始(不妨认为是320单位宽的的x轴偏移),最终移动到x轴偏移为0的屏幕位置。切换过程中的每一帧,JavaScript线程都需要发送一个新的x轴偏移量给主线程。

由于JS就是一单线程的,他会优先处理自页面的dom构建,以及componentDidMount中的事件处理,这是JS线程就无法处理转场动画了,知道处理完自页面释放之后在来处理动画,这就导致切换页面出现卡顿现象

解决方案:
使用API InteractionManager,它的作用就是可以使本来JS的一些操作在动画完成之后执行,这样就可确保动画的流程性

InteractionManager简介

1、可以提升用户体验和交互效果的模块InteractionMnager(交互管理器)

2、基本内容

使用InteractionManager可以让一些耗时的任务在交互操作或者动画完成之后进行执行,这样使用可以保证我们的JavaScript的动画效果可以平滑流畅的执行。可以大大提升用户体验。

在应用开发中我们可以如下进行执行任务

InteractionManager.runAfterInteractions(() => {

  //执行耗时的同步任务

});

该模块和其他相关的调度方法对比:

  • requestAnimationFrame():执行控制动画效果的代码
  • setImmediate/setTimeout():设置延迟执行任务的时间,该可能会影响到正在执行的动画
  • runAfterInteractions():延迟执行任务,该不会影响到正在执行的动画效果

触摸系统中的单点或者多点触控都是交互动作,耗时任务会在这些触摸交互动作执行完成之后或者取消以后回调runAfterInteractions()方法进行执行。

runAfterInteractions任务也可以接收一个普通的回调函数或者一个带有gen方法并且返回一个Promise的PromiseTask对象。如果参数是PromiseTask对象,那么任务是异步执行的,也会阻塞。该会等着当前任务执行完毕以后才能执行下一个任务。

默认情况下,队列任务会一次性在setImmediate方法中批量执行。如果你通过setDeadline方法设置一个时间值,那么任务会在延迟该设定值时间进行执行。这时候会调用setTimeout方法进行挂起任务并且阻塞其他任务的执行。这样可以给触摸交互等操作留出时间更好的相应用户操作。

3、方法与属性

  1. runAfterInteractions(task) 静态方法,在用户交互和动画结束以后执行任务
  2. createInteractionHandle() 静态方法,创建一个句柄(处理器),通知管理器,某个动画或者交互开始了
  3. clearInteractionHandle(handler:Handle) 静态方法,进行清除句柄,通知管理器,某个动画或者交互结束了。
  4. setDeadline(deadline:number) 静态方法, 设置延迟时间,该会调用setTimeout方法挂起并且阻塞所有没有完成的任务,然后在eventLoopRunningTime到设定的延迟时间后,然后执行setImmediate方法进行批量执行任务
  5. Events:CallExpression
  6. addListener:CallExpression

4、具体使用

开发中关于卡顿的解决:
先在render中渲染一个空视图,等转场动画完成以后,再去渲染世纪的视图

代码如下:

//页面引入InteractionManager
import {
    AppRegistry,
    StyleSheet,
    Text,
    ScrollView,
    Image,
    Alert,
    TouchableOpacity,
    InteractionManager,
    View,
    ImageBackground,
    DeviceEventEmitte
} from "react-native";
class Redeem extends Component {
    constructor(props) {
        super(props);
        this.state = {
            renderPlaceholderOnly: true//交互管理器延时控制标识
        };

    }
    /**
     * 页面初始化先渲染空视图减少页面转场时间
     */
    _renderPlaceholderView = () => {
        return (
            <View
                style={{
                    flex: 1,
                    justifyContent: "center",
                    alignItems: "center"
                }}>
                <Image
                    style={{ width: 150, height: 150, backgroundColor: "#f5f5f5" }}
                    source={require("./../../img/task/loading.gif")}
                />
            </View>
        );
    };
    render() {
        //首先渲染空视图
        if (this.state.renderPlaceholderOnly) {
            return this._renderPlaceholderView();
        }
        return(
            //页面dom
            ...
        )
    }
    componentDidMount() {
        //转场动画完成之后改变标记值,重新渲染dom
        InteractionManager.runAfterInteractions(() => {
            this.setState({ renderPlaceholderOnly: false });
        });
    }
}

二、事件监听

场景:在页面跳转是如从A页面路由跳转到B页面,需要在B页面更新了数据,返回A页面是需要更新A页面数据

问题:
由于react路由是基于hash来实现的,从A页面路由跳转到B页面,会在URL上拼接页面对应的hash值,从B页面在返回A页面时,去除URL上的hash值,相当于改变了URL,此时A页面会重新加载,从而达到刷新页面的效果;

但是react-navigation的路由跳转是基于栈来实现的,A路由跳转B,就是一个压栈的过程,就是把页面B push到栈顶,从B返回到A是就是pop弹栈,此时对于页面A来说是无感知的,就是把B页面从栈中pop出去,此时对于开发者来说就无法感知B页面的返回操作了;

解决方案:

使用RN神器发送和接收事件DeviceEventEmitter

具体使用如下:

页面A:

//首 先导入DeviceEventEmitter组件
import {
    StyleSheet,
    Text,
    ScrollView,
    Image,
    Alert,
    TouchableOpacity,
    TouchableWithoutFeedback,
    TouchableHighlight,
    InteractionManager,
    View,
    FlatList,
    SectionList,
    DeviceEventEmitter
} from "react-native";
//要在A页面写一个接收消息的方法
//在didmount方法中写好监听/接收方法,当有消息发送时,这就会接收到,并执行相应方法
componentDidMount() {
        //收到监听 事件监听
        //从新手任务页面或者积分兑换页面返回需要监听 然后更新任务首页总积分
        //原因:新手任务页面和积分兑换页面都会设计积分的变更,但是返回任务首页时不会刷新页面,
        //所以需要监听返回状态,在主动调用接口
        this.listener = DeviceEventEmitter.addListener("left", (e) => {
            //e就是从页面B发送过来的数据
            if (e) {
                this.getAllTntegral();
            }
            Toast.info(e);
        });

    }
}
//当然,别忘了要卸载
componentWillUnmount() {
        // 移除监听
        this.listener.remove();
}
页面B
//首 先导入DeviceEventEmitter组件
import {
    StyleSheet,
    Text,
    ScrollView,
    Image,
    Alert,
    TouchableOpacity,
    View,
    ImageBackground,
    InteractionManager,
    FlatList,
    DeviceEventEmitter
} from "react-native";
//可根据自己的业务场景来实现消息发送的时机,本人用法是在B页面返回时,给A页面发消息通知
class NewTask extends Component {
    static navigationOptions = ({ navigation }) => ({
        headerTitle: `${navigation.state.params.title}`,
        headerLeft: (
            <TouchableOpacity
                style={styles.action}
                onPress={() => {
                    //给监听事件赋值
                    DeviceEventEmitter.emit("left", "back");
                    navigation.goBack();
                }}>
                <View style={styles.headerLeft} />
            </TouchableOpacity>
        )
        // headerBackTitle: `${navigation.state.params.title}`
    });
}

原文地址:https://www.cnblogs.com/gongchenghui/p/9299373.html

时间: 2024-08-04 09:46:27

RN性能优化及事件监听的相关文章

Netty事件监听和处理(上)

通过介绍,你会了解到: 事件监听.NIO.线程模型等相关概念: Netty总体结构: 事件监听和处理: 项目实践总结: 本篇先介绍下前两节,下一篇介绍后两节. 本篇最后会说明下福利的抽取规则,大家积极参与 >_< 相关概念 Netty是一个NIO框架,它将IO通道的建立.可读.可写等状态变化,抽象成事件,以责任链的方式进行传递,可以在处理链上插入自定义的Handler,对感兴趣的事件进行监听和处理. 所以,先介绍下事件监听.责任链模型.socket接口和IO模型.线程模型等基本概念,对后面理解

浅谈事件监听

自从学了事件监听之后,觉得他真是个好东西~~,为什么呢? 首先说下他的好处:第一点:他可以实现对未来元素事件的绑定[?未来元素:就是在绑定事件时,页面上还不存在的元素]:第二点:减少事件绑定,提高性能[我们知道前端主要是解决性能优化和兼容问题的,所有这个就挺重要了] 下面来捋一捋事件监听一个比较全面的见解? 1.关于事件 事件分为DOM 0级事件和Dom 2级事件,DOM2级事件也叫做事件监听.DOM 0级事件的缺点是如果事件相同 后者的事件会覆盖前者的事件,DOM2级事件可以解决这个问题 do

关于v4包的Fragment过渡动画的事件监听无响应问题解决

项目中部分功能模块采用了单Activity+多Fragment模式,当Fragment切换时,需要在过渡动画执行完后做一些操作,通常就是在自己封装的FragmentBase中重写onCreateAnimation方法,创建一个Animation对象,并添加动画的事件监听.而最近升级了v4包后,突然发现添加的动画事件监听无响应了.通过查看源码,发现在v4包中关于Fragment管理类FragmentManagerImpl中,在获取Animation对象后,也添加了对动画的监听事件,也就覆盖了我自己

JS 事件绑定、事件监听、事件委托详细介绍

事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 在DOM中直接绑定事件 我们可以在DOM元素上绑定onclick.onmouseover.onmouseout.onmousedown.onmouseup.ondblclick.onkeyd

javascript 兼容W3c和IE的添加(取消)事件监听方法

事件作为javascript本身的一个必备功能,在目前javascript的使用中是无处不在的,基本要只要写到javascrpt,就会用到javascript事件.下面就说说javascript中的添加事件监听和取消事件监听的方法,当然也要做到兼容W3c和IE.下面是兼容的代码: [javascript] view plain copy //添加事件监听兼容函数 function addHandler(target, eventType, handler){ if(target.addEvent

前端基本知识(四):JS的异步模式:1、回调函数;2、事件监听;3、观察者模式;4、promise对象

JavaScript语言将任务的执行模式可以分成两种:同步(Synchronous)和异步(Asychronous). “同步模式”就是一个任务完成之后,后边跟着一个任务接着执行:程序的执行顺序和排列顺序是一直的:”异步模式”则完全不同,每一个任务都有一个或者多个回调函数(callback),前一个任务结束的时候,不是执行下一个任务,二十执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务顺序不一致的,异步的. 在浏览器端,耗时时间长的操作都应该异步执行,避免浏览器数去

JS 中的事件绑定、事件监听、事件委托

事件绑定 要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数.所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称. 在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数. 在DOM中直接绑定事件 我们可以在DOM元素上绑定onclick.onmouseover.onmouseout.onmousedown.onmouseup.ondblclick.onkeyd

GUI编程笔记05:GUI事件监听机制原理和举例说明

1.事件监听机制:       A:事件源          事件发生的地方       B:事件             就是要发生的事情       C:事件处理       就是针对发生的事情做出的处理方案       D:事件监听       就是把事件源和事件关联起来 2.举例:人受伤事件. 事件源:人(具体的对象)         Person p1 = new Person("张三");         Person p2 = new Person("李四&qu

javascript事件监听与事件委托

事件监听与事件委托 在js中,常用到element.addEventListener()来进行事件的监听.但是当页面中存在大量需要绑定事件的元素时,这种方式可能会带来性能影响.此时,我们可以用事件委托的方式来进行事件的监听. 每个事件都经历三个阶段 捕获 到达目标 冒泡 事件委托需要用到事件的冒泡,冒泡就是事件发生时,上层会一层一层的接收这个事件. 如下页面结构: <body> <div id="div1"> <div id="div2"