背景
公司要开发一款新的应用,一方面希望能像H5一样跨平台和动态更新,另一方面又不满原生嵌入H5这种性能瓶颈,于是我便开始进行一系列调研,包括RN、Flutter、Uni-app等,本篇文章主要给大家分享下在调研RN中的一些结果。主要内容有:
1)RN 是什么?
2)RN 为什么出现,它带来了哪些好处?
3)RN如何实现跨平台?
4)RN热更新机制。
5)RN是如何解决H5性能瓶颈的?
6)RN快速上手步骤。
7)RN有哪些缺点,未来发展方向。
RN是什么?
RN是React Native的简称。其中React(有时叫React.js或ReactJS)是一套开源JavaScript 库(也可以称为前端 UI 框架),而React Native则是React向移动端的延伸。我们可以认为React Native分为两层,React 层是一套JavaScript 库;Native层则扮演桥梁的功能,根据平台不同映射为不同的原生控件;通过这种设计实现了—开发者构建的RN代码可以在不同的移动平台上(暂时只支持Android和IOS)运行,也就是Learn once, write anywhere。
问题A:为什么不是Write once, run everywhere呢?
RN 为什么出现,它带来了哪些好处?
React 最初来自 Facebook 内部的广告系统项目,随着页面与逻辑越来越复杂项目的前端开发遇到了巨大的挑战:当更新一个页面元素,需要了解整个view DOM树、所有关联节点的业务等才能正确更新UI与业务逻辑,当页面复杂时候,这是一个相当费力的事情,所以他们会有一种技巧,修改部分UI与逻辑,但是全部重新渲染,这就是react的初步设想。
接下来Facebook对市场上所有 JavaScript MVC 框架进行审核,发现MVC需要通过C来绑定View,监听View的数据变化,从而实现级联(相关View的)更新。 但是在小框架上,V和 C往往很难分离,分离反而不利于阅读和理解代码。因此Facebook对主流的JS框架很不满,任性的他们就决定自己写一套,用来架设 Instagram的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。React设计目的就是解决目前JS框架不够直观和业务逻辑复杂的弊病,所以React 最有价值的是声明式的,直观的编程方式。
RN如何实现跨平台?
在围绕原生与H5交互实践聊聊Android混合开发,我们有谈到H5能够实现跨平台的原因在于Android和IOS都实现了一个内置浏览器-WebView,分别通过Android和IOS上的这两个浏览器来解释运行H5。
同理RN也是分别在Android和IOS实现了两个翻译器Jsc.so和JavaScriptCore,通过这两个翻译器RN代码就可以翻译成为Android或者IOS代码,从而在android或IOS系统上运行。具体可以参考文章:React Native运行原理解析。
总揽所有的跨平台技术,其实和指挥多国联合部队差不多,指挥官发出命令,每个国家的部队接收这个命令时,必须有对应的翻译系统将它翻译成本国语言,才能使之得到执行。所以每个翻译系统精准度、稳定度、效率都是衡量整个(多国作战系统)跨平台技术优异的指标。至于在每个国家如何构建精准、稳定、高效的解释翻译系统,则需要根据指挥官语言和这个国家语言进行针对性设计。我接下来会有文章对翻译解释系统进行讨论,欢迎大家在这里留言,提供想法。
RN热更新机制
谈到RN不得不谈一谈它的热更新机制,在围绕原生与H5交互实践聊聊Android混合开发,有提到H5与生俱来的结构设计,是把数据和数据展示方式都当做数据从服务器下发,也就是说H5页面可以包含所有的业务逻辑和数据信息,它天生具有热更新机制。
RN热更新的原理就是从服务器拉取新的JS bundle文件,并重新加载。JS bundle,bundle: 捆,JS bundle一捆JS,可以认为是H5页面集合,对比H5一页页的拉取,RN是一次性拉取下来,并给这个集合一个版本号,如果版本更新再去拉取新的JS页面集合,一次性拉取下来之后,js就是本地文件,利用RN解释翻译器(JavaScriptCore)去解释执行RN代码(JS 集合中的JSX代码)。所以RN热更新的核心技术还是构建RN的解释器翻译器。
谈到这里,我有一个问题,问题B: 热更新思想的实质是什么?它的发展方向是什么?
个人见解:
热更新思想= 所有东西都是数据流( every thing is data flow)
热更新核心技术-> 构建跨平台解释翻译系统
大家有什么想法,请在下方评论。
RN是如何解决h5性能瓶颈的?
首先我们需要了解H5性能瓶颈是怎么造成的?对比原生,H5包含信息更加自由丰富,这也导致:
1)从服务器获取信息时候,H5页面需要更多的数据量和校验步骤,相应的需要消耗更多的时间;
2)本地解析渲染(显示)时候,H5需要更复杂的解析渲染规则,相应的需要耗费更多的时间。
RN可以认为是H5(JS)的特殊子集合。那么RN解决H5性能瓶颈,自然也是从这两点进行出发的。
首先我们看问题1),它关键在于减少每次网络请求的数据量。这一点我们其实已经从一个名词( JS bundle)中猜测到,RN是相当于把一个业务模块(可以自定义分包)需要的H5页面弄成一个集合JS bundle,当首次加载这个页面的时候一次性下载到本地,当再次加载该页面的时候,就直接从本地获取就行了,如果业务的页面逻辑等需要调整,那就启动热更新,更新JS bundle,更新过程跟第一次加载一样。在这种方式下,当非首次加载JS bundles时候,从网络请求的数据跟原生基本差不多,甚至一些特定情况比原生还要少。所以问题1)的答案就是是运用批量缓存的方式来减少平时使用过程中网络请求数据量。
接下来我们再研究问题2),它关键在于缩短JS代码解析渲染的时间, 但是由于JS的灵活性与复杂性,虽然很多团队在研究这个问题,但它的解析渲染始终比原生慢上许多。在这个问题上facebook另辟蹊径,它认为既然原生渲染快,那么RN可以舍弃JS渲染,通过映射的方式把RN代码中的控件一个个映射为原生控件进行渲染,比如我们用RN代码写一个Button,那么实际在运行的时候RN会生成一个映射表,将这个Button分别映射为android和ios的button,相关设置的属性也进行了同步映射。所以问题2)的答案就是将RN控件映射为原生控件,从而利用原生的渲染能力。
从上面可以看到,其实RN解决问题1)和2)并不是硬干,都是从实际情况入手做了一些另辟蹊径的解决方案,那我这里留下几个问题给读者:
问题C:如果业务逻辑更新特别频繁,是否适合用RN?
问题D:RN能否做到android端和IOS端统一,如果不能为什么,如果可以怎么做 ?
问题E:RN代码在本地运行时是通过javaScriptCore边解释边运行,还是提前翻译成原生代码,甚至是机器码放在本地运行,facebook为什么这样设计?
RN快速上手步骤
一 、RN开发环境搭建
请参考RN中文网-环境搭建
二、RN本地运行与调试
1 、运行请参考RN中文网运行篇
这里我仅仅解释下命令react-native run-android和(IOS 本地运行需要)npm start
这两个命令都会在电脑上起一个服务进程Watchman,Watchman是由Facebook提供的监视文件系统变更的工具,这个服务进程起来后就会监听RN文件目录下文件是否变化,如果变化就把相关内容打包发送给手机,从而实现了本地热更新。(你的电脑相当于远程的服务器,手机从你电脑上获取新的JS bundle)。
2、关于打开本地热更新,摇一摇手机打开本地热更新功能选择界面,或者在命令提示符窗口输入:adb shell input keyevent 82。
3、 关于调试请参考RN中文网-调试
三、RN开发基本流程
React 是一个全新思路的前端 UI 框架,它完全接管了 UI 开发中最为复杂的局部更新部分,擅长在在复杂场景下保证高性能;同时,它引入了基于组件的开发思想,从另一个角度来重新审视 UI 的构成。通过这种方法,不仅能够提高开发效率,而且可以让代码更容易理解,维护和测试,所以RN开发的一些流程和原则与原生有很大不同。
1、绘制原型图,如果有直接用;
2、列出所有需要使用的基础组,一个基础组件只负责一个功能;
3、对基础组件进行分层;
4、搭建静态页面;
5、分析页面之间的关系;
6、确定数据显示者和拥有者 ;
7 、确定状态机变量最小集;
8 、确定各个事件的接收者和处理者;
9 、实现界面业务逻辑。
三、RN开发原则与规范
1、不能用“this.state.某状态机变量名 = 值”来刷新页面。this.state.值是可以获取到具体state的值,但是它不会引发render,所以这样赋值是不会生效,牢牢记住state值是跟页面UI变化绑定的一起的。应该使用this.setState进行赋值, 因为setState异步的,而且会进行state状态合并,所以最好的是,时间顺序无关的state可以如下赋值:
this.setState({
bannerImageUrl:jsonContent.androidImgUrl,
}
如果一个state值连续改变,请传递一个方法到setState中确保setState能够正确执行,如下:
this.setState((state) => {
return {
number: number+1,
}
})
this.setState((state) => {
return {
number: number+2,
}
})
2、如果跟ui无关的变量尽量不要定义在state中 ,会导致页面绘制。
3、组件成员变量定义请用如下方式:this.myProperty='HaHa'
4、尽量让自定义的组件成为无状态的React native 组件,无状态意思就是没有状态机 state。
5、尽量创建多个无状态(只负责渲染数据)的React native 组件,将他们封装在 一个有状态的组件中,并把有状态值通过props 传给无状态的 react-native 组件。
6、让UI中可变数据来源是状态机变量和属性。
7、 遵循自底而上的设计开发原则。
RN有哪些缺点,未来发展方向
从上面可以看出RN最大优势是开发者友好(声明式的,直观的编程方式),它基本原理在于提供一套从JS到原生的映射,从而实现书写JS代码拥有接近原生的运行效果。
但是这种另辟蹊径的方式下的缺点也是有不少的,最明显的就是两端的不统一,JS在Android平台和IOS平台都映射为Android和IOS的原生View进行渲染,Android和IOS原生不一致的话,那么自然会出现双端不一致;其次这种多端桥接的方式,等于说RN兼容JS版本和原生版本,JS版本和原生版本不断进化千万万,做RN的同学很能体会到这种兼容性的坑;最后就是做RN需要会Android、IOS、JS这一整套全懂,学习成本高。
为解决多端不统一的问题,谷歌大佬直接又做了一套全新的渲染机制,实现了一次书写,多端运行,但是由于多种原因比如dart语言的普及率、Flutter热更新被禁止等,Flutter还未真正普及起来。个人认为RN的未来也会向Flutter进行靠拢,一方面强化自己的优势(入手快,直观、用户友好),另一方面尽量实现多端统一。
网友评论