引言
上一次,我们成功的把 React 应用渲染到了 Canvas 上面。今天我们野心更大一点,来实现一个简单的 React Native,我们叫他 Extremely Tiny React Native。我们最终实现的效果如下图所示:
所对应的 React 代码如下:
1 | import React from 'react' |
前置知识准备
实现这个 Extremely Tiny React Native 所需要的两个核心知识是:
- JavaScriptCore
- React Custom Renderer
其中 React Custom Renderer 之前已经介绍过了,详见React 源码解析之 Custom Renderer,所以这里我们只简单的介绍下 JavaScriptCore。
JavaScriptCore
JavaScriptCore
(以下简称 JSCore)是 iOS 上的 JavaScript(以下简称JS)执行引擎,它建立起了 Objective-C(以下简称OC)和 JS 两门语言之间沟通的桥梁。接下来我们举例来看看它的基础用法:
例一:执行 JS 代码:
1 | JSContext *jsCtx = [[JSContext alloc] init]; |
例二:JS 调用 Native 的方法:
1 | jsCtx[@"log"] = ^(NSString *msg){ |
如上,我们在 JSContext
对象上挂载了一个 log
方法后,JS 代码中即可直接调用该方法。
例三:JS 使用 Native 中的对象:
首先自定义个协议
JSPersonProtocol
继承自JSExprot
,并定义需要暴露给 JS 的属性和方法:1
2
3
4
5
6
7
8
NS_ASSUME_NONNULL_BEGIN
// 定义一个协议,可以理解为接口
@protocol JSPersonProtocol <JSExport>
- (NSString *)whatYouName;
@end新建一个
Person
对象,实现协议和方法:1
2
3
4
5
6
7// 继承 NSObject,实现 JSPersonProtocol 协议
@interface Person : NSObject<JSPersonProtocol>
@property (nonatomic, copy)NSString *name;
- (NSString *)whatYouName;
@end
NS_ASSUME_NONNULL_END
1 |
|
- 使用:
1 | Person *p = [[Person alloc]init]; |
实现原理
在React 源码解析之 Custom Renderer中介绍过 Custom Renderer 需要实现一些宿主相关的接口,如:createInstance
、appendChild
等。
在我们的 Extremely Tiny React Native 中,这些接口都是通过 JSCore 向 Native 发送 JSON 格式的消息,真正的操作在 Native 侧来完成。而传递消息的实现方法上文例三已经介绍过了,这里简单贴一下代码:
1 | @protocol BridgeProtocol <JSExport> |
1 | - (void) send:(NSString *)msg { |
1 | self.jsContext[@"RNBridge"] = [[Bridge alloc] initWithRootViewController:self]; |
比如调用 createInstance
时,JS 会通知 Native 侧当前的操作是 createView
(或 createText
),即需要在 Native 侧创建一个 RNView
(或 RNText
) 的对象:
1 | // RNView |
同时分配给该对象一个唯一标识符 id
,方便后续进行其他操作时通过该 id
来找到相应的对象,且该 id
会挂载在 FiberNode
的 stateNode
属性之上(这一步是 React 为我们实现的)。
当调用 appendChild
时,React 会传入 parent
和 child
(注意这里的 parent
和 child
是前面提到的 id
):
1 | appendChild: function (parent, child) { |
剩下的其他接口按部就班实现就可以了,完整代码详见 tiny-react-native。
总结
利用 JavaScriptCore 和 React Custom Renderer,我们实现了一个 React Native,不过目前它还非常的简单,后续可以考虑进一步增强如下功能:
- 支持 style 属性
- 支持 Reload 的功能
- 支持 Flexbox 布局