Open Ulanxx opened 4 years ago
跨平台的优势:
跨平台的优势
真正的原生应用:产生的不是网页应用,不是混合应用,而是一个原生的移动应用。 快速开发应用:相比原生漫长的编译过程,Hot Reload简直不要太爽。 可随时呼叫原生外援:完美兼容Java/Swift/OC的组件,一部分用原生一部分用RN来做完全可以。 跨平台:一套业务逻辑代码可以稳定运行在两个平台。 节省劳动力:为企业节省劳动力。。。(不知道算不算好事儿)。
真正的原生应用
快速开发应用
可随时呼叫原生外援
跨平台
节省劳动力
可以看出RN和Flutter还是呈五五开的发展态势。 github:
但是Flutter是在18年底才发行了以第一个稳定版,而React Native是15年就已经推出。这么一看,Flutter突然🔥起来,就1年的时间就挤掉了RN的大半市场,今天我们一起看一下,这两个跨平台的框架究竟有什么神奇的地方。
React Native是带着React的光环出生的一个跨平台框架,具备React的一切新特性,让从Ionic与HBuilder的时代走过的Hybrid的开发欲罢不能。因为他能通过React的代码与通用的业务逻辑,编写一套完全原生的App应用,而且APP的使用感受与OC/JAVA编写的Native APP完全一致。
react-native init demo
React Native和React的基本业务逻辑与项目结构是相通的,除了组件是从react-native的包里引用,样式是css的子集,其他的都是页面的生命周期,渲染逻辑,diff都与React无异。
import React from 'react'; import { SafeAreaView, StyleSheet, ScrollView, View, Text, StatusBar, } from 'react-native'; import { Header, LearnMoreLinks, Colors, DebugInstructions, ReloadInstructions, } from 'react-native/Libraries/NewAppScreen'; const App = () => { return ( <> <StatusBar barStyle="dark-content" /> <SafeAreaView> <ScrollView contentInsetAdjustmentBehavior="automatic" style={styles.scrollView}> <Header /> {global.HermesInternal == null ? null : ( <View style={styles.engine}> <Text style={styles.footer}>Engine: Hermes</Text> </View> )} <View style={styles.body}> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Step One</Text> <Text style={styles.sectionDescription}> Edit <Text style={styles.highlight}>App.js</Text> to change this screen and then come back to see your edits. </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>See Your Changes</Text> <Text style={styles.sectionDescription}> <ReloadInstructions /> </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Debug</Text> <Text style={styles.sectionDescription}> <DebugInstructions /> </Text> </View> <View style={styles.sectionContainer}> <Text style={styles.sectionTitle}>Learn More</Text> <Text style={styles.sectionDescription}> Read the docs to discover what to do next: </Text> </View> <LearnMoreLinks /> </View> </ScrollView> </SafeAreaView> </> ); }; const styles = StyleSheet.create({ scrollView: { backgroundColor: Colors.lighter, }, engine: { position: 'absolute', right: 0, }, body: { backgroundColor: Colors.white, }, sectionContainer: { marginTop: 32, paddingHorizontal: 24, }, sectionTitle: { fontSize: 24, fontWeight: '600', color: Colors.black, }, sectionDescription: { marginTop: 8, fontSize: 18, fontWeight: '400', color: Colors.dark, }, highlight: { fontWeight: '700', }, footer: { color: Colors.dark, fontSize: 12, fontWeight: '600', padding: 4, paddingRight: 12, textAlign: 'right', }, }); export default App;
Android/IOS目录分别承载这各自应用架构与bundle入口,src部分会打包成jsbundle,然后通过Native的入口注入。
看到这里会有这样的一个疑问为什么js代码可以运行在APP中?
为什么js代码可以运行在APP中?
是因为RN有两个核心 JSC引擎:1 → 因为RN的包里一个有JS执行引擎(WebKit的内核JavaScriptCore),所以它可以运行js代码。(前期是JSC的环境,在0.60.x之后添加了Hermes作为js引擎)。 干货 | 加载速度提升15%,携程对RN新一代JS引擎Hermes的调研 React Native JSC源码 JSI通信:其实就是JSBridge,作为JS与Native的桥梁,运行在JSC环境下,通过C++实现的Native类的代理对象,这样就可以实现JS与Native通信。
是因为RN有两个核心
干货 | 加载速度提升15%,携程对RN新一代JS引擎Hermes的调研
React Native JSC源码
所以:JSC/Hermes会将作为JS的运行环境(解释器),JS层通过JSI获取到对应的C++层的module对象的代理,最终通过JNI回调Java层的module,在通过JNI映射到Native的函数。
RN Native Android Module源码
RN Native IOS Module源码
所以,RN中所有的标签其实都不是真是的控件,js代码中所有的控件,都是一个“Map对中的key”,JS通过这个key组合的DOM,放到VDOM的js数据结构中,然后通过JSBridge代理到Native,Native端会解析这个DOM,从而获得对应的Native的控件。
例子:实现判断应用是否开启通知,如果未打开通知则进入设置页面开启通知。
5.1 IOS端
IOS端
#import <Foundation/Foundation.h> #import <React/RCTEventEmitter.h> @interface RNDataTransferManager : RCTEventEmitter <RCTBridgeModule> @end
#import "RNDataTransferManager.h" @implementation RNDataTransferManager RCT_EXPORT_MODULE(); // 判断notification是否开启 RCT_EXPORT_METHOD(isNotificationEnabled:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { BOOL isEnable = NO; UIUserNotificationSettings *setting = [[UIApplication sharedApplication] currentUserNotificationSettings]; isEnable = (UIUserNotificationTypeNone == setting.types) ? NO : YES; return resolve(@(isEnable)); } // 进入设置开启Notification RCT_EXPORT_METHOD(gotoOpenNotification) { [self goToAppSystemSetting]; }
注意两个宏:
RCT_EXPORT_METHOD:用来设置给JS导出的Native Module名字。
RCT_EXPORT_METHOD
RCT_EXPORT_MODULE:给JS提供的方法通过RCT_EXPORT_METHOD()宏实现,必须明确的声明要给 JavaScript 导出的方法,否则 React Native 不会导出任何方法。
RCT_EXPORT_MODULE
RCT_EXPORT_METHOD()
5.2 Android端
Android端
首先新建一个JavaModule类继承ReactContextBaseJavaModule。
public class RNDataTransferManager extends ReactContextBaseJavaModule { private static ReactApplicationContext reactContext; public static RNDataTransferManager rnDataTransferManager; public static String currentBindAlias = ""; public RNDataTransferManager(@Nonnull ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; } public static RNDataTransferManager getInstance() { if (null == rnDataTransferManager) { rnDataTransferManager = new RNDataTransferManager(reactContext); } return rnDataTransferManager; } @Nonnull @Override public String getName() { return "RNDataTransferManager"; } @ReactMethod public void isNotificationEnabled(Promise promise) { if (promise != null) { if (MainApplication.getContext() != null) { if (NotificationManagerCompat.from(MainApplication.getContext()) .areNotificationsEnabled()) { Log.e("push", "推送开启 isNotificationEnabled -> true"); promise.resolve(true); } else { Log.e("push", "推送未开启 isNotificationEnabled -> false"); promise.resolve(false); } } else { promise.resolve(false); } } } @ReactMethod public boolean gotoOpenNotification() { if (MainApplication.getContext() == null) { return false; } Intent intent = getSetIntent(MainApplication.getContext()); PackageManager packageManager = MainApplication.getContext().getPackageManager(); List<ResolveInfo> list = packageManager.queryIntentActivities(intent, 0); if (list != null && list.size() > 0) { try { MainApplication.getContext().startActivity(intent); return true; } catch (Exception e) { return false; } } return false; } }
写好了Native Module之后需要注册模块。
1)首先通过ReactPackage的createNativeModules来注册模块。
package com.mengtuiapp.mms.bridge; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; public class DataTransferPackage implements ReactPackage { private RNDataTransferManager transferModule; @Nonnull @Override public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) { List<NativeModule> nativeModules = new ArrayList<>(); transferModule = new RNDataTransferManager(reactContext); RNDataTransferManager.rnDataTransferManager = transferModule; nativeModules.add(transferModule); return nativeModules; } @Nonnull @Override public List<ViewManager> createViewManagers(@Nonnull ReactApplicationContext reactContext) { return Collections.emptyList(); } }
2)然后让你的应用拿到注册到的package,需要在Application的getPackages方法中提供。
@Override protected List<ReactPackage> getPackages() { List<ReactPackage> packages = new PackageList(this).getPackages(); packages.add(new DataTransferPackage()); packages.add(new RNInstallApkPackage()); packages.add(new RNUserAgentPackage()); packages.add(new RNKeyboardAdjustPackage()); packages.add(new CodePush(mContext.getString(R.string.InnotechCodepushKey), mContext, this.moduleId, BuildConfig.DEBUG, mContext.getString(R.string.InnotechCodepushServerUrl))); return packages; }
5.3 JS端调用
NativeModules.RNDataTransferManager.gotoOpenNotification() 就可以前往应用设置页面打开通知。
相比Hybrid性能更高、因为都是原生组件的渲染。 从render到virtual dom的过程都是React驱动,具备React的一切优秀特性,可以使用React的社区优秀工具。 项目搭建起来了,用JS写APP又具有原生的渲染效率简直爽,一份代码Android、IOS、web都可以适配(毕竟vDom层是一样的,jsbridge就随你魔改了)。 相比原生的编译速度,开发JS使用HotReload简直太爽了。
跨平台,但是Android、IOS毕竟是不同的系统与生态,组件与功能都有一些跨平台的差异,RN的原生组件的平台差异性很大。 性能问题:动画性能不好、列表数据量大性能不好,主要集中在低端机,大数据列表快速滑动会有白屏,动画层级多在Android低于30fps的情况频繁。 白屏问题,加载bundle的时间会有一个白屏出现,需要手动改Native代码。 开发业务功能不需要原生能力,但是开发一个完整的跨平台项目,是需要具备一定的双端原生能力(有很多要写Native的,许多功能和组件也需要自己封装) 这也是RN做的不好的地方,版本迭代太慢,不痛不痒的迭代了5年了,很多问题还是没有解决。这也是Flutter为什么这么火的原因。
Flutter使用Dart作为开发语言,作为一个AOT框架,Flutter是在运行时直接将Dart转化成客户端的可执行文件,然后通过Skia渲染引擎直接渲染到硬件平台。如果说RN是为开发者做了平台兼容,那Flutter更像是为开发者屏蔽了平台的概念。RN需要被转译为本地对应的组件,而Flutter是直接编译成可执行文件并渲染到设备。Flutter可以直接控制屏幕上的每一个像素,这样就可以避免由于使用JSBridge导致的性能问题。
三要素: Dart语言开发。 任何Dart代码都是AOT运行前编译成本地可执行文件,使用Skia(渲染引擎)直接渲染到本机。 不使用原生的组件,具有自己的widget库,开发时构建自己的widget树来画页面。
三要素:
直接演示。
今天我们主要看了一下两个框架开发时的代码结构,与代码书写形式,还有简单了解了一下它是怎么运行。那之后如果小伙伴想继续去学习,或做一个自己的应用,还有以下几个方面需要注意:
1. APP初始化与生命周期状态。 2. 数据持久化 - 数据管理、SP、本地数据库。 3. 碎片化处理。 4. 打包三要素:Android(混淆、签名、加固),IOS(生成证书、导入证书、使用证书)。 5. 拆包、热更新、原生集成。
Flutter因为自带了渲染引擎,理论上是要比RN渲染效率要高,但是其实实际使用上,在性能过剩的移动端设备中,并没有出现特别大的差异,而Facebook的团队在Flutter的持续施压之下也决定重构底层,并在最近几个版本有了一些进步,所以大家有兴趣的都可以研究一下。
跨平台APP开发实践(RN、Flutter)
0. 前言
跨平台的优势
:使用Flutter的应用:闲鱼、美团B端、阿里(FlutterGo、AliFlutter、淘宝特价版)、字节(今日头条、西瓜视频、皮皮虾)
使用React Native的应用:携程、美团C端、字节(今日头条)、手机QQ、FaceBook
可以看出RN和Flutter还是呈五五开的发展态势。 github:
但是Flutter是在18年底才发行了以第一个稳定版,而React Native是15年就已经推出。这么一看,Flutter突然🔥起来,就1年的时间就挤掉了RN的大半市场,今天我们一起看一下,这两个跨平台的框架究竟有什么神奇的地方。
1. React Native的入门与实践
React Native是带着React的光环出生的一个跨平台框架,具备React的一切新特性,让从Ionic与HBuilder的时代走过的Hybrid的开发欲罢不能。因为他能通过React的代码与通用的业务逻辑,编写一套完全原生的App应用,而且APP的使用感受与OC/JAVA编写的Native APP完全一致。
1. 搭建环境
2. 创建应用
react-native init demo
3. 写法与代码结构
React Native和React的基本业务逻辑与项目结构是相通的,除了组件是从react-native的包里引用,样式是css的子集,其他的都是页面的生命周期,渲染逻辑,diff都与React无异。
Android/IOS目录分别承载这各自应用架构与bundle入口,src部分会打包成jsbundle,然后通过Native的入口注入。
4.RN的运行机制
看到这里会有这样的一个疑问
为什么js代码可以运行在APP中?
所以:JSC/Hermes会将作为JS的运行环境(解释器),JS层通过JSI获取到对应的C++层的module对象的代理,最终通过JNI回调Java层的module,在通过JNI映射到Native的函数。
RN Native Android Module源码
RN Native IOS Module源码
所以,RN中所有的标签其实都不是真是的控件,js代码中所有的控件,都是一个“Map对中的key”,JS通过这个key组合的DOM,放到VDOM的js数据结构中,然后通过JSBridge代理到Native,Native端会解析这个DOM,从而获得对应的Native的控件。
5.怎么实现一个Native Bridge的功能。(先不讲)
5.1
IOS端
注意两个宏:
RCT_EXPORT_METHOD
:用来设置给JS导出的Native Module名字。RCT_EXPORT_MODULE
:给JS提供的方法通过RCT_EXPORT_METHOD()
宏实现,必须明确的声明要给 JavaScript 导出的方法,否则 React Native 不会导出任何方法。5.2
Android端
首先新建一个JavaModule类继承ReactContextBaseJavaModule。
写好了Native Module之后需要注册模块。
1)首先通过ReactPackage的createNativeModules来注册模块。
2)然后让你的应用拿到注册到的package,需要在Application的getPackages方法中提供。
5.3 JS端调用
6. 这样做的优势和问题
优势:
问题:
7. 那么Flutter怎么做的
Flutter使用Dart作为开发语言,作为一个AOT框架,Flutter是在运行时直接将Dart转化成客户端的可执行文件,然后通过Skia渲染引擎直接渲染到硬件平台。如果说RN是为开发者做了平台兼容,那Flutter更像是为开发者屏蔽了平台的概念。RN需要被转译为本地对应的组件,而Flutter是直接编译成可执行文件并渲染到设备。Flutter可以直接控制屏幕上的每一个像素,这样就可以避免由于使用JSBridge导致的性能问题。
如果是页面级的应用来说,Flutter是不需要任何原生代码来写组件,所有组件和页面都可以通过Flutter直接写好。
7.1 Flutter代码结构
7.2 Flutter运行与调试
直接演示。
8. 总结
今天我们主要看了一下两个框架开发时的代码结构,与代码书写形式,还有简单了解了一下它是怎么运行。那之后如果小伙伴想继续去学习,或做一个自己的应用,还有以下几个方面需要注意:
Flutter因为自带了渲染引擎,理论上是要比RN渲染效率要高,但是其实实际使用上,在性能过剩的移动端设备中,并没有出现特别大的差异,而Facebook的团队在Flutter的持续施压之下也决定重构底层,并在最近几个版本有了一些进步,所以大家有兴趣的都可以研究一下。