React-Native 目前是一个不错的多平台开发和快速开发的解决方案,它解决了不少开发上的痛点,而且做为 facebook 大厂出品,维护上有保障,所以学习 RN 是有好处的。固然,它目前还存在着不少的问题,可是均可以经过一些方案来规避和解决。RN 目前来讲对 iOS 开发者比较友好,而 Android 开发环境则比较难以配置。node
学什么都得先来个 HelloWorld,RN 官方文档对 HelloWorld 已经讲的很详细了。react
> brew install node > brew install watchman > npm install -g react-native-cli > react-native init HelloWorld
而后就自动建立了对应的 iOS 和 Android 工程,笔者本身使用的是 node4-lts 版本,watchman 用于监视文件是否改变,不过笔者记得之前版本 watchman 是经过 npm 安装的,而且不是强制要求。可能如今版本改动了,不装就不能运行。android
{ "name": "HelloWorld", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { "react": "15.3.2", "react-native": "0.34.1" } }
这是 package.json 文件内容,里面依赖了 react 和 react-native。打开 iOS 工程,里面基本和普通的 iOS 工程差很少,Libraries 目录包括了 node_modules 文件夹下的依赖工程,React.xcodeproj
工程内含两段脚本,一个是 RCTJSCProfiler
分析,一个是启动本地 node 服务器。AppDelegate.m 以下ios
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSURL *jsCodeLocation; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"HelloWorld" initialProperties:nil launchOptions:launchOptions]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES; }
主要的区别就是使用 RCTRootView 做为 RN 渲染视图添加到控制器上。而且指定了 bundleURL 和 moduleName,看过源码的应该知道,bundleURL 就是本地或者远程 jsbundle 路径,而 moduleName 就是 js 代码中注册的模块名,jsBundleURLForBundleRoot:fallbackResource
则是返回本地或者远程的 jsbundle 路径,这里就不细讲了,只要点开源码一看就懂。npm
Android 环境配置比 iOS 麻烦多了,由于 iOS URL 等配置都是明文放在 AppDelegate 代码中的,哪里建立了 RootView 都很清楚,而安卓就比较麻烦,MainApplication
实现了 ReactApplication
接口,主要是生成了 mReactNativeHost
成员变量。mReactNativeHost
持有 ReactInstanceManager
类变量,初始化代码以下json
ReactInstanceManager.Builder builder = ReactInstanceManager.builder() .setApplication(mApplication) .setJSMainModuleName(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setRedBoxHandler(getRedBoxHandler()) .setUIImplementationProvider(getUIImplementationProvider()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
这里能够看到指定了 jsMainModuleName 和是否显示开发者菜单的参数,再来看 MainActivity,它继承了 ReactActivity,只重写了一个方法react-native
/** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "HelloWorld"; }
再来看它的父类 ReactActivity,里面有一个成员变量 ReactActivityDelegate,它接受两个参数初始化xcode
new ReactActivityDelegate(this, getMainComponentName());
这就变相的将组件名传入了 mDelegate。再进入 ReactActivityDelegate
,就能找到服务器
private @Nullable ReactRootView mReactRootView;
这就是 RootView,而后它的初始化以下app
protected void loadApp(String appKey) { if (mReactRootView != null) { throw new IllegalStateException("Cannot loadApp while app is already running."); } mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), appKey, getLaunchOptions()); getPlainActivity().setContentView(mReactRootView); }
这里的初始化实际上就是对应了 iOS 中 rootView 的初始化,只不过 Android 是 startReactApplication 而不是经过构造函数传递参数的方式。而且,主要的参数都是经过 ReactInstanceManager 来存储管理,appKey 实际上就是组件名,等同于 iOS 中的 moduleName
,而 ReactInstanceManager
初始化中的 setJSMainModuleName
才是 jsbundle 名称。Android 甚至没有在代码中指定 remote server jsbundle 路径的地方,目前只能进入 app 而后打开 dev setting
来修改远程服务器地址,固然,也多是笔者没有找到,若是有找到的朋友请在评论中告诉我。并且 ReactInstanceManager
初始化后面还有一小段代码
String jsBundleFile = getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName())); }
发现了没,iOS 下远程 jsbundle 和本地 jsbundle 是经过一个方法统一处理的,而 Android 则须要两个参数维护。iOS 下 RN 只提供了 RootView,开发者只须要处理好 RootView 暴露的方法就好了,更加简单,而 Android 环境下则是提供了 ReactActivity 类来作封装,而且将参数拆成了好几个类,很是的麻烦。
手动 iOS 整合步骤以下:
package.json
建立带有 subspecs 的 Podfile
用于引入所须要的第三方库,这须要 CocoaPods,虽然也能够手动 link 第三方库,可是目前笔者推荐使用 CocoaPods
index.ios.js
RCTRootView
固然,还有其余步骤,好比 ATS 的问题,不过一个合格的开发者应当都能完成,看上面这些步骤,你会发现实际上在 iOS 下,手动整合和 HelloWorld 基本是一致的。
手动整合步骤以下
package.json
增长 com.facebook.react:react-native:+
和 maven {}
到 build.gradle
index.android.js
ReactRootView
是否发现了,在 Android 环境下,HelloWorld 工程和手动整合指南是不同的,最主要的区别在于 HelloWorld 工程是继承 ReactActivity 来产生 Activity 的,而手动整合则是能够随便作,甚至能够在 Fragment 上面建立 RootView。
就目前而言,RN 在 iOS 下不管是表现仍是编码难度,都优于 Android 环境,Android 官方甚至没有一个很好的启动 App 方式,只能 react-native run-android
。并且在性能方面,iOS 通过良好的编码和优化,是可以达到原生代码的水平的,而 Android 则有很多问题,可是 RN 虽然有这样那样的问题,它对整个跨平台开发是有很大贡献的,让开发者有机会多端开发,能涉足其余平台,并且相信将来这些问题都是能解决的。