402's Dojo

React Native入门实例教程 - 开始开发

最近一段时间在学习和实践React Native,做一些有趣有用总结和大家分享。这个系列文章可以在这里找到。

入口文件

React Native项目初始化时默认的入口文件是index.ios.js,根据我们在React Native入门实例教程 - 从现有项目迁移中的配置,运行项目后会将index.ios.js以及所以来的JS文件打包成index.ios.bundle
通过Node.js Server访问这个bundle,就可以得到所需要的视图。

了解有关这个调试Server以及React Native Debug相关内容,可以访问React Native入门实例教程 - 调试系统

视图构建

RCTBridge - 桥接

OCBridge是利用JavascriptCore直接调用JS代码的。OC层实现这个类的是RCTBridge,目前的代码是使用RCTContextExecutor作为具体的执行者。JavascriptCore是iOS7才开放的接口,不过目前的代码还有另外一套RCTWebViewExecutor,里面用的是通过UIWebView调用JS,可能是为了以后兼容旧版本的iOS。使用JavascriptCore最显而易见的优势就是,整个执行过程都可以在后台线程执行,事实上RCTContextExecutor单独开了一个名为「com.facebook.React.JavaScript」的线程,供自己使用。

RCTRootView - 视图容器

Native代码中通过RCTRootView这个类引用JS视图,示例代码如下:

NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];
// For production use, this `NSURL` could instead point to a pre-bundled file on disk:
//
//   NSURL *jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
//
// To generate that file, run the curl command and add the output to your main Xcode build target:
//
//   curl http://localhost:8081/index.ios.bundle -o main.jsbundle
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName: @"SampleApp"
initialProperties:nil
launchOptions:nil];

上述代码中是通过访问server localhost:8081来实现加载jsbundle,也可以直接将打包好的jsbundle文件集成在项目资源中,然后从mainBundle直接引用。这样做的问题是每次js代码变动都需要更新jsbundle并重新编译。这种方式通常用于App发布时默认资源包的集成。jsbundle的详细创建方式参见React Native入门实例教程 - 项目打包发布

目录组织

如果项目采用Native与React Native混编的方式,那么入口文件index.ios.js可能不止一个,每个React Native模块都会有一个入口文件。我的做法是将所有JS代码分模块放在与React Native开发环境同级的ReactComponents目录下,例如:

- ReactComponents
- CustomModule1
- custom1.ios.js
- custom1.android.js
- imgs/
- CustomModule2
- custom2.ios.js
- custom2.android.js
- imgs/

由于调试server的根目录指向React Native的环境目录(node_modulespackage.json等文件所在的目录),因此入口文件访问的路径为:http://localhost:8081/ReactComponents/CustomModule1/custom1.ios.bundle。注意是文件后缀是bundle而不是js

容器视图

在大型项目中解耦第三方库和主项目依赖是非常重要的一件事情,它能让你的项目架构更清晰,适应更多的变化。为了解耦React Native的源码和你的项目,可以定义如ReactViewReactViewController这样的类,将RCTRootView等相关逻辑封装进去,甚至添加自定义的导航控制器。

Native Modules

对于一些Native项目已有的控件,我们希望也能通过JS代码来访问他们,React Native已经给我们提供了成熟的转换方法,实现起来非常简单。

转换方法

  1. 实现RCTBridgeModule协议;
// CalendarManager.h
#import "RCTBridgeModule.h"
@interface CalendarManager : NSObject <RCTBridgeModule>
@end
  1. 添加RCT_EXPORT_MODULE()宏,将当前模块导出;
// CalendarManager.m
@implementation CalendarManager
RCT_EXPORT_MODULE();
@end
  1. 将需要暴露给JS的方法用RCT_EXPORT_METHOD包起来。
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
  1. 接下来就可以在JS代码中访问这个module了。
var CalendarManager = require('react-native').NativeModules.CalendarManager;
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');

值得注意的问题

定义navtive module的一个问题是无法访问它的类方法,或者控制初始化方法,感觉还是不灵活。
比如在一个Native的VC中嵌入ReactView,这个ReactView无法访问到它的VC,这样会给我们的设计上带来一些困难。

RCTConvert

RCT_EXPORT_METHOD的参数支持标准的JSON类型:

  • string (NSString)
  • number (NSInteger, float, double, CGFloat, NSNumber)
  • boolean (BOOL, NSNumber)
  • array (NSArray) of any types from this list
  • map (NSDictionary) with string keys and values of any type from this list
  • function (RCTResponseSenderBlock)
    自定义的类型如果想在JS中作为参数需要先做convert。
    automatic type conversion feature

小技巧

  1. 将方法标记成deprecated
  2. JSONKit在64位上有问题,React Native在加载bridge过程中解析JSON时优先会用JSONKit,因此暴露了这个问题,更新最新支持64位的JSONKit解决此问题。
  3. JSX陷阱|JSX与HTML的区别
  4. Swift类获取OC Runtime:@objc(CalendarManager)

Where to go

React Native入门实例教程 - iOS导航专题

参考链接

评论