当前位置: 首页 > Web前端 > JavaScript

为前端写的react-native开发入门指南

时间:2023-03-26 22:57:24 JavaScript

前言本文主要介绍react-native(以下简称RN)的介绍,以及与前端的异同本文不涉及功能的具体实现。会选择使用RN,它对应的特性和常见的Web前端资源的区别,生态互通因为使用的语言是JS和react,对于前端来说可以无缝切换,他也可以使用各种前端封装在JS客户端,Android和iOS是同一套代码热更新。很多选择使用RN是因为有热更新。下面简单介绍一下热更新。在运行APP时,我们可以通过收到的通知实时更换js层。更换完成后一般需要重启APP。这时候可以询问用户,也可以在下次重启时重新加载新的js代码,这样可以保证用户使用的js环境可以比较新。如果是原APP的更新,则需要更新。用户去应用商店重新下载,支持原生RN通过bridge与原生进行交互。页面层面集成到原生APP的很多组件中。方法是调用native方法/组件,相比webview有更好的性能。RN的跨终端框架横向对比与Flutter的简单对比环境无论是RN还是Flutter,都需要Android和IOS的开发环境,即JDK、AndroidSDK、Xcode等环境配置。不同的是RN需要npm、node、react-native-cli等配置。Flutter需要fluttersdk以及AndroidStudio/VSCode上的Dart和Flutter插件。对于前端来说,RN环境还是比较友好的。实现原理在Android和IOS上。默认情况下,Flutter和ReactNative**都需要原生平台Activity/ViewController支持,属于原生层面的“单页应用”,**它们最大的区别在于UI构建:RN:ReactNative是一组UI框架。默认情况下,ReactNative会在Activity下加载JS文件,然后在JavaScriptCore中运行解析Bundle文件布局,最后栈出一系列原生控件进行渲染。简单来说就是通过写JS代码配置页面布局,然后ReactNative最终会解析渲染成原生控件,比如ViewGroup/UIView对应的标签,ScrollView/对应的标签UIScrollView,以及ImageView/UIImageView对应的标签等等。Flutter:Flutter中的大部分Widget都是平台无关的。开发者基于Fr??amework开发应用,Framework运行在Engine上,由Engine适配和支持。这个跨平台支持过程其实就是把FlutterUI中的Widget“数据化”,然后在Engine上通过Skia直接绘制到屏幕上。类似于前端canvas绘图,这一段来自文章:https://www.jianshu.com/p/da8...缺点RN:不完全兼容W3C规范。例如,在W3C中,可以轻松设置圆角的大小。thickness,border是实现的,dottedlines,但是在客户端,这个实现起来比较困难。因此,此类技术只能支持有限的W3C标准。js运行性能瓶颈。数据通信的性能瓶颈。Flutter:无法动态更新。内存和数据包大小使用情况。学习成本高,生态不足。js运行环境在使用RN时,JS代码会运行在两种不同的环境中:在大多数情况下,RN使用JavaScriptCore,这是Safari使用的JavaScript引擎。但是JavaScriptCore并没有在iOS上使用即时编译技术(JIT),因为iOS中的应用程序没有权利拥有可写和可执行的内存页(因此不能动态生成代码)。使用Chrome进行调试时,所有JavaScript代码都在Chrome中运行,并通过WebSocket与本机代码通信。此时的运行环境是V8引擎。所以我们在开始调试的时候,会和官方的运行环境有些差异。RN有一个内置的Babel转换器。因此,我们不需要为很多语法配置babel。语法环境可以直接使用。这里可以看到具体的配置定时器。RN中有一个用于动画的定时器:InteractionManager,原生应用感觉流畅的一个重要原因是它是交互的,并且避免了动画过程中的繁重操作。在RN中,它是有限的,因为我们只有一个JavaScript执行线程。因此InteractionManager确保在进行繁重的工作之前处理所有交互和动画。InteractionManager.runAfterInteractions(()=>{//...需要长时间同步执行的任务...});与其他几个定时器相比:requestAnimationFrame():用于执行一段时间的控制视图动画代码setImmediate/setTimeout/setInterval():稍后执行代码。请注意,这可能会延迟当前正在进行的动画。runAfterInteractions():稍后执行代码而不延迟当前正在进行的动画。HermesEngineHermes是一个为RN应用程序优化的新的开源JavaScript引擎。对于许多应用程序,启用Hermes引擎可以优化启动时间,减少内存使用和空间使用。Hermes特色预编译字节码(引擎加载二进制代码比运行JS脚本效率更高)没有JIT编译器(减少引擎的大小,优化内存使用,但直接运行JS脚本的性能比V8和JSC差)formobile终端垃圾回收策略优化原理传统的JavaScript引擎通常会按照上图所示的方式完成代码执行。编译阶段只完成了babelescape和minifycompression,产品还是JavaScript脚本。解释执行任务需要在运行时完成(比如V8引擎,在运行时将JavaScript编译成本地机器码)明显的缺点是需要在运行时解释执行,甚至需要占用系统资源来执行编译任务。Hermes引擎采用aot编译方式,将解释编译过程预编译到编译阶段,只在运行时完成机器码的执行,大大提高了运行效率。RN中原生ui组件的一个优势是可以插入原生组件,提高APP的性能。如果我们想在js中使用ImageView,我们需要这些步骤:创建一个ViewManager的子类。实现createViewInstance方法。导出视图属性设置器:用@ReactProp(或@ReactPropGroup)注释。在应用程序包的createViewManagers中注册这个视图管理器类。实施JavaScript模块。以上是Android的添加,相对而言iOS会更简单:首先创建一个RCTViewManager的子类。添加RCT_EXPORT_MODULE()宏标志。实现-(UIView*)view方法。//RNTMapManager.m#import#import@interfaceRNTMapManager:RCTViewManager@end@implementationRNTMapManagerRCT_EXPORT_MODULE(RNTMap)-(UIView*)view{return[[MKMapViewalloc]初始化];}@end在JS中使用//MapView.jsimport{requireNativeComponent}from'react-native';//requireNativeComponent自动解析'RNTMap'为'RNTMapManager'exportdefaultrequireNativeComponent('RNTMap');//MyApp.jsimportMapViewfrom'./MapView.js';...render(){return;}这个是简单的展示,传值的属性比较多另外判断对于原生组件,js也可以给移动端传值,添加监听事件(包括promise回调),对应的移动端也可以形成一个完整的两端通信系统。上面链接原生库是react-native-splash-screen库的安装和链接,我们在使用三方原生库的时候,需要做一个链接函数。我们用RN发布的所有库都在仓库的Libraries文件夹下。其中一些是纯Javascript代码,您只需导入它们即可使用。此外,一些库是基于一些本机代码实现的。您必须将这些文件添加到您的应用程序中,否则应用程序将在您使用这些库时产生错误。链接是手动链接项目中依赖项的替代方法。手动链接是一件很麻烦的事情。Android和iOS的解决方案还是不一样的。您可以查看现状。幸运的是,如果我们使用的RN库是0.60以上的,就不需要使用链接命令了。Android中会自动链接,iOS中可以使用cocoapods下载原生包cocoapods。简而言之,CocoaPods是Swift和Objective-CCocoa项目的依赖管理器。以此类推,可以认为是npm即是。使用cocoapods时,需要Podfile文件,可以类比package.json,然后通过命令podinstall(类似于npminstall)下载。下载完成后会存放在Pods文件下,另外还有lock文件:Podfile.lock,Manifest两份.lock悄悄话,如果没有翻墙工具pod下载会变得很麻烦,经常stuck路由管理RN中常用的路由管理有两种:一种是ReactNavigation,一种是react-native-navigation,两者的区别在于前者使用JS代码,通过monorepo的结合,通过对react-native-screens、react-native-reanimatedv2等库,最终形成接近原作的体验。至于为什么大部分都放在js端,有什么好处,我会在下面的热更新部分进行说明,后者使用原生容器作为路由接口,比如或者,带来原生的性能、特性和体验,但是当我们使用这个库或者想集成另一个库的时候,会带来一些麻烦,前端有什么异同。APP的路由中会有一个概念栈(stack),这是与web最大的区别。这里我们用一张图来介绍一下:当我们进入一个新的页面时,之前的页面不会被销毁(大多数情况下),它会将新的页面添加到栈中,所以在APP中,我们必须时刻小心内存泄漏。Update这个是RN中最常用也是最大优势的功能之一——热更新热更新方案总体来说分为三种方案:react-native-pushyReactNative中文站上线react-native-code-push+AppCenter完全免费,国内可能速度慢,适合个人开发者react-native-code-push+code-push-server适合公司自建热更新服务器注意事项热更新:苹果App允许热更新苹果的开发者协议,为了不影响用户体验,必须使用静默更新GooglePlay不能使用静默更新,必须弹框告知用户App已经更新。中国安卓市场必须采用静默更新(如有弹框提示,App将被拒绝,理由为“请上传最新版本的二进制应用程序包”)。react-native-code-push只更新资源文件,不会更新java和ObjectiveC,所以npm升级依赖包版本时,如果使用了依赖包使用的本地化实现,那么应用版本号就必须改变,然后应用必须重新编译发布到应用商店。一般来说,手机的热更新过程:检测、下载、重启等,都是npm包react-native-code-push提供的API。大,分包吧,本文不深入分析APP更新。上文我们提到,原生组件更新后,需要重新下载APP,那么如何更新方便呢?这里我用的是我之前写的那个。本文原理如下:原文请点这里。上面提到的热更新和APP更新在electron上也有相应的实现。对于web端的同学来说,这是一个值得参考的地方。APP中还有许多其他差异。有很多细节区别于web端,这里给出一些debug解决方案。开发过H5的人应该对vconsole不陌生。RN中还有一个vconsole组件,用于调试,打印console,查看请求,显示各种信息等。还封装了RN的一个vconsole插件:react-native-vconsole,综合了多个插件的优点-插件。是安卓机物理按键操作的特有功能。它是物理键。用户可以直接点击物理键返回。返回首页时需要显示提示,再次按下退出,需要特殊适配:BackHandler.addEventListener('hardwareBackPress',this.handleBackPress)handleBackPress=()=>{if(//ifis第一页){consttimestamp=newDate().valueOf()if(timestamp-firstClick>2000){firstClick=timestampToastAndroid.show('再次按下退出',ToastAndroid.SHORT)returntrue//returntrue,意思是阻止默认操作}}returnfalse}沉浸式状态栏手机端会有一个状态栏,这是一个非常有视觉冲击力的功能。pool的变化,这部分是状态栏在RN时,我们通过这个API来控制:当然是为了适应各种一些情况(在APP中需要在页面进入、离开等小的功能变化时修改状态栏),有时候有些页面需要透明的状态栏,也需要一些特殊的设置。很多时候这个组件不是直接使用的,需要打包来适应大部分的页面版本变化,RN里面有几个版本有比较大的breakingchange。0.59-0.60直接升级,这两个版本有很多breakingchanges。iOS端最大的变化就是package变成了CocoaPods(上面说了)使得我们的package依赖也需要相应的升级(预计50%以上的package升级),所以影响范围基本是整个项目。在Android端,链接方法发生了变化。另外是build.gradle、settings.gradle、AndroidX配置的修改。这里介绍一下官方的升级工具:https://react-native-communit...他可以比较对应的版本,显示里面的变化。0.68另一个升级是从0.67升级到0.68。在本次版本变更中,RN进行了四项调整:JavaScriptInterface(JSI)——通信更新Fabric——新的渲染系统TurboModules——Native模块增强CodeGen——静态类型检查因为这是一个很底层的修改,可能会引起对所有现有组件,影响范围几乎涵盖了全球新架构。这里我们就说说0.68更新了什么。原生的JavaScriptInterface(JSI)上面的架构我已经讲过了,它有一些问题:目前RN使用BridgeModule进行通信(同样需要数据转换和解码),发送的消息本质上是异步的,也就是说,如果即时性比较高的操作,比如拖拽,是会造成丢帧的。在新架构中,Bridge将被JavaScriptInterface取代它是一个轻量级的通用层,用C++编写,JavaScriptEngine可以用它直接执行或调用native原理。在JSI中,native方法会通过C++HostObjects暴露给JS,而JS可以持有这些对象的对象引用,利用这些引用直接调用相应的方法。一个简单的例子,这类似于Web中的JS代码可以保存对任何DOM元素的引用并在其上调用方法:constcontainer=document.createElement('div');如果你有electron的经验,原来的一些通信方式和electron中主进程和渲染进程的通信是一样的。在Fabric的老架构中,RN布局是异步的,这就导致在宿主视图中渲染嵌套的RN视图,会出现布局“抖动”的问题。.新架构与JSI一样,采用跨平台方案,共享核心C++实现。一个简单的解释就是JSI的UI版本。当然,还有一些其他的好处:渲染器具有多优先级和同步事件的能力,可以提高用户交互的优先级,以确保他们的操作得到及时处理。ReactSuspense的集成让你可以更直观的在React中编写请求数据代码。允许您在RN中使用React并发可中断渲染。更容易实现RN的服务端渲染。TurboModules在之前的架构中,所有JS使用的NativeModule(比如蓝牙、地理位置、文件存储等)都必须在应用打开前进行初始化,这意味着即使用户不需要某些模块,它仍然必须在启动时初始化。TurboModules基本上是对这些旧的Native模块的增强,如前所述,现在JS将能够持有这些模块的引用,因此JS代码只能在需要时加载相应的模块,这可以显着减少RN应用程序的启动时间.CodeGenCodegen主要用于保证JS代码和C++JSI能够正常通信。通过使用类型化的JS作为参考源,CodeGen将定义可由Turbo模块和Fabric使用的接口。此外,Codegen将生成Native代码以减少运行时开销。skia现在RN也学习了Flutter的skia渲染,但是还处于alpharelease阶段,是一个值得期待的方向。目前库支持Image,Text,Shader,Effects,Shapes,Animations等操作。仓库,不仅仅是为了RN版本的兼容性,还有各种需要定制和缺乏维护的业务需求,即使出现了issue在及时修复性能上,RN的性能确实比webview好很多,但也比native差。在非常复杂的场景中,你需要使用原生的页面/组件。ControlRN目前用的最多的是antd版本的组件库,可以支持的场景非常多,但是也缺乏人员维护兼容性。有很多上面提到的RN升级问题。其实RN还没有到1.0.0正式版,所以很多API都会有breakchange。总结文章和其他问题没有在文章中详细解释,比如Android打包、签名、iOS上架、常用代码、图片优化方法、字体解决、启动画面、长列表问题等等,不过这些也是细节,和主体的对比基本如前所述,RN目前的情况还是很不错的,未来和flutter的竞争不假。作为向移动端扩展方向的前端,RN是最好的选择,参考https://www.jianshu。com/p/da8...https://blog.csdn.net/tyuiof/...https://www.infoq.cn/article/...https://reactnavigation.org/d...https://juejin.cn/post/706373...