美文网首页
RN的大体认识

RN的大体认识

作者: 千寻与小米 | 来源:发表于2020-02-14 00:45 被阅读0次

    初识React Native

    我们都知道React是一套开源JavaScript 库(也可以称为前端 UI 框架),而React Native则是React向移动端的延伸。我们可以认为React Native分为两层,React 层是一套JavaScript 库;Native层则扮演桥梁的功能,根据平台不同映射为不同的原生控件;通过这种设计实现了—开发者构建的RN代码可以在不同的平台上(Android和IOS)运行,也就是Learn once, write anywhere。Java(Write once, run anywhere)

    RN是如何解决H5性能瓶颈的?

    首先我们需要了解H5性能瓶颈是怎么造成的?对比原生,H5包含信息更加自由丰富,这也导致:
    1、从服务器获取信息时候,H5页面需要更多的数据量和校验步骤,相应的需要消耗更多的时间;
    2、本地解析渲染(显示)时候,H5需要更复杂的解析渲染规则,相应的需要耗费更多的时间。

    RN可以认为是H5(JS)的特殊子集合。那么RN解决H5性能瓶颈,自然也是从这两点进行出发的。
    首先我们看问题1,它关键在于正常使用时候减少网络请求的数据量。这一点我们其实已经从一个名词( JS bundle)中猜测到,RN相当于进入一个模块时候(可以自定义分包)把需要更新的的H5页面打包成一捆(js bundle,可能是一页,也可能是好几页)一次性更新,当再次加载该页面的时候,就直接从本地获取就行了。在这种方式下,当非首次加载JS bundles时候,从网络请求的数据跟原生基本差不多,甚至一些特定情况比原生还要少。所以问题1的答案就是是运用(批量)缓存的方式来减少平时使用过程中网络请求数据量。
    接下来我们再研究问题2,它关键在于缩短JS代码解析渲染的时间,为解决这个问题,Facebook采用了两个重要举措:
    1、引入虚拟DOM减少不必要的渲染;

    虚拟DOM、虚拟硬盘、虚拟机等计算机术语中提到的“虚拟”基本都是为了解决“真实”操作起来复杂、繁琐、代价高等问题而生,它们都能够将相对简单方便的虚拟操作映射成为真实的行动。虚拟DOM的出现之前,一次页面更新可能有十几个DOM更新操作,前前后后更新了十几次,大大浪费了渲染性能。出现虚拟DOM之后,一次更新操作中的10次更新DOM的动作,不会立即操作DOM,而是将这10次更新更新到虚拟DOM中,通过对比找出前后两次虚拟DOM的不同(diff算法),然后把不同一次性映射为真实DOM的更新。

    2、舍弃JS渲染,通过映射的方式把RN代码中的控件一个个映射为原生控件进行渲染。

    由于JS的灵活性与复杂性,虽然很多团队在研究它的渲染问题,但它的解析+渲染始终比原生慢上许多。于是facebook工程师另辟蹊径,它认为既然原生渲染快,那就用原生渲染代替JS渲染,RN做好桥接就好了。比如我们用RN代码写一个Button,那么实际在运行的时候RN会生成一个映射表,将这个Button分别映射为android和ios的button,相关设置的属性也进行了同步映射。

    两个很重要的概念

    Props(属性)

    大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为props(属性)。
    以常见的基础组件Image为例,在创建一个图片时,可以传入一个名为source的prop来指定要显示的图片的地址,以及使用名为style的prop来控制其尺寸。

    比如Android里的Activity的话,Props有点类似Intent ,props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。但是功能比Intent 强大,

    State(状态)

    我们使用两种数据来控制一个组件:props和state。props是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。 对于需要改变的数据,我们需要使用state。
    一般来说,你需要在constructor中初始化state(译注:这是ES6的写法,早期的很多ES5的例子使用的是getInitialState方法来初始化state,这一做法会逐渐被淘汰),然后在需要修改时调用setState方法。

    相同点

    1. 不管是props还是state的改变,都会引发render的重新渲染。
    2. 都能由自身组件的相应初始化函数设定初始值。

    不同点

    1. 初始值来源:state的初始值来自于自身的getInitalState(constructor)函数;props来自于父组件或者自身getDefaultProps(若key相同前者可覆盖后者)。
    2. 修改方式:state只能在自身组件中setState,不能由父组件修改;props只能由父组件修改,不能在自身组件修改。
    3. 对子组件:props是一个父组件传递给子组件的数据流,这个数据流可以一直传递到子孙组件;state代表的是一个组件内部自身的状态,只能在自身组件中存在。

    生命周期

    我们先来看初始化,在初始化的过程中,会按顺序调用下面5个函数。

    getDefaultProps:组件实例创建前调用,多个实例间共享引用。注意:如果父组件传递过来的Props和你在该函数中定义的Props的key一样,将会被覆盖。
    getInitalState:组件示例创建的时候调用的第一个函数。主要用于初始化state。注意:为了在使用中不出现空值,建议初始化state的时候尽可能给每一个可能用到的值都赋一个初始值。
    componentWillMount:在render前,getInitalState之后调用。仅调用一次,可以用于改变state操作。
    render:组件渲染函数,会返回一个Virtual DOM,只允许返回一个最外层容器组件。render函数尽量保持纯净,只渲染组件,不修改状态,不执行副操作(比如计时器)。
    componentDidMount:在render渲染之后,React会根据Virtual DOM来生成真实DOM,生成完毕后会调用该函数。在浏览器端(React),我们可以通过this.getDOMNode()来拿到相应的DOM节点。然而我们在RN中并用不到,在RN中主要在该函数中执行网络请求,定时器开启等相关操作

    初始化完成之后,组件将会进入到运行中状态,运行中状态我们将会遇到如下几个函数:

    componentWillReceiveProps(nextProps):props改变(父容器来更改或是redux),将会调用该函数。新的props将会作为参数传递进来,老的props可以根据this.props来获取。我们可以在该函数中对state作一些处理。注意:在该函数中更新state不会引起二次渲染。
    boolean shouldComponentUpdate(object nextProps, object nextState):该函数传递过来两个参数,新的state和新的props。state和props的改变都会调到该函数。该函数主要对传递过来的nextProps和nextState作判断。如果返回true则重新渲染,如果返回false则不重新渲染。在某些特定条件下,我们可以根据传递过来的props和state来选择更新或者不更新,从而提高效率。
    componentWillUpdate(object nextProps, object nextState):与componentWillMount方法类似,组件上会接收到新的props或者state渲染之前,调用该方法。但是不可以在该方法中更新state和props。
    render:跟初始化的时候功能一样。
    componentDidUpdate(object prevProps,object prevState):和初始化时期的componentDidMount类似,在render之后,真实DOM生成之后调用该函数。传递过来的是当前的props和state。在该函数中同样可以使用this.getDOMNode()来拿到相应的DOM节点。如果你需要在运行中执行某些副操作,请在该函数中完成。componentWillReceiveProps(nextProps):props改变(父容器来更改或是redux),将会调用该函数。新的props将会作为参数传递进来,老的props可以根据this.props来获取。我们可以在该函数中对state作一些处理。注意:在该函数中更新state不会引起二次渲染。

    销毁阶段只有一个函数,很简单

    componentWillUnmount:组件DOM中移除的时候调用。在这里进行一些相关的销毁操作,比如定时器,监听等等。

    生命周期 跳用次数 是否setState
    getDefaultProps 1(全局调用一次)
    getInitialState 1
    componentWillMount 1
    render >=1
    componentDidMount 1
    componentWillReceiveProps >=0
    shouldComponentUpdate >=0
    componentWillUpdate >=0
    componentDidUpdate >=0
    componentWillUnmount 1

    优势

    1、 跨平台
    2、 组件
    3、 简化的生命周期
    4、 迭代的速度(高峰期)
    5、 性能(出去初始化的时间的话)
    6、 Redux (状态管理的库等)
    7、 支持原生
    8、 动画
    9、 开源

    遇到的问题

    首先,必须承认,RN不是JS工程师就可以搞定的。 不懂IOS还好, 团队没有懂android整个架构的工程师, android就可能有各种问题。 优化android是一件非常繁琐的事情,得知道优化android需要很多的黑科技。

    其次, RN也不是Native工程师可以顺利搞定的。 native工程师做的时候会束手束脚,其实这和npm生态有关。 在开发native产品时候, 大部分需要用到的架构library库,都是官方搞定的。 而RN如果说, 架构很好, 那是社区搞定的。 所以对于刚刚接触RN的native工程师, 会觉得RN非常难以架构, 没有架构的工具,

    而且Android技术也在不断优化中,就一个Databinding特性就不是ReactNative所能支持的,更别提其他的RxAndroid响应式编程了这些东西了,涉及底层的功能需要Android和Ios双端单独开发,JS调用,整体性能仍不如原生;

    我觉得ReactNative更像是原生开发的一个补充,一些App的新的尝试的可能。毕竟用纯ReactNative开发商业化的App,我怎么都觉得有点不靠谱,有过Aribnb 大公司的前车之鉴

    趟坑总结:

    同样的代码在Android 和IOS 的响应速度以及请求的成功率不一样:

    TypeError: Network request failed:

    https://blog.csdn.net/qq_32312317/article/details/80868118

    **react-native link XX **

    react-native 并不成熟

    React Native比Android或iOS更不成熟(或者说是稳定)。 它更新,更有野心,并且更新速度非常快。 虽然React Native在大多数情况下都能很好地工作,但有些情况下,它的不稳定可能会显示出来,并且会使原生开发中的一些微不足道的事情变得非常困难。 不幸的是,这些情况很难预测,可能需要几小时到几天的时间才能解决。这个问题,Airbnb 团队 自行维护RN分支

    由于React Native的不成熟,他们有时需要修正React Native源码。 除了向React Native做出贡献之外,他们还必须维护一个分支,以便他们能够快速合并更改并使版本崩溃。 在过去两年中,他们不得不在React Native之上添加大约50次提交。 这使得升级React Native的过程非常痛苦。

    重构之难

    JavaScript是一种无类型的语言。 缺乏类型安全性难以扩展,JavaScript被忽略的副作用是重构非常困难且容易出错。 重命名props,特别是带有通常名称的props,如通过多个组件传递的onClick或props,是准确重构的噩梦。 更糟糕的是,重构在生产中而不是在编译时崩溃,很难为其添加适当的静态分析。

    react-native开放源代码库

    在Android上,许多React Native库也要求您使用node_modules的相对路径,而不是发布与社区所期望的不一致的maven工件。 需要动态去链接一些库,
    RN的很多库,需要单独对IOS或者Aandroid 要进行特殊的处理,但是一般给出的文档都不是很详细
    比如 react-native-webview react-native-community/react-native-masked-view react-native link react-native-device-info 都需要去react-native link xx or pod install 原理就是将一个node_module里面的工程,作为Android的project


    image.png

    初始化时间

    在React Native首次可以渲染之前,必须初始化其运行时。 不幸的是,即使在高端设备上,也需要几秒钟的时间。 这使得使用React Native进行启动屏幕几乎是不可能的。通过在应用程序启动时初始化React Native来缩短第一次渲染时间。与原生屏幕不同,渲染React Native需要至少一个完整的主线程 - > js - >Yoga布局线程 - >主线程往返行程,然后才有足够的信息来第一次渲染屏幕。iOS平均初始p90呈现280ms,Android平均440ms。 在Android上,使用通常用于共享元素转换的postponeEnterTransition API来延迟显示屏幕直到它被渲染。 在iOS上,我遇到了问题,从React Native快速设置导航栏配置。 因此,我为所有React Native屏幕过渡添加了50ms的仿真延迟,以防止配置加载后导航栏闪烁。

    手势

    在涉及复杂手势的屏幕上使用React Native,因为Android和iOS的触摸子系统足够不同,以至于提出统一的API对整个React Native社区来说都具有挑战性。需要按照不同的os 单独设置

    莫名其妙的崩溃

    不得不面对一些难以解决的非常奇怪的崩溃。 例如,Hermes 引擎下,react-navigation-stack库不支持,在官方这些文档上并没有体现出来,不支持这个的原因我也是在假设,正常的代码打成bundle 就运行不了!

    资料之难

    似乎RN的已经大势所去,国内外对目前版本的一些文章博客很少,github 很多优秀的开源项目基本上都是三四年的,已经很陈旧,没有一个能顺利的运行起来


    image.png

    相关文章

      网友评论

          本文标题:RN的大体认识

          本文链接:https://www.haomeiwen.com/subject/qpprfhtx.html