介绍(闲扯)
React Native是javascript和native混合编程的一套框架, 是由facebook开源出来的, 通过javascrip和React提供跨平台的native开发支持. 之前有一篇RN的简单介绍, 由于目前网络上对RN深入的分析较少(只有几篇内部的开发分享很赞, 列在了参考中), 其他大多是偏于主观感受. 因此试图整理一篇偏干的文章, 分析RN到底做了什么, 如有纰漏欢迎讨论.
本文不是入门教程的文章, 喜欢看入门操作教程的朋友们可以直接去RN官方站点, 介绍的很详细, 而且在不断更新. 因为我对iOS更熟悉, 所以分析是由iOS的视角切入的, 各位javascript朋友请不吝赐教. 使用的RN版本是目前最新的v0.17.0-rc版本.
正文
从RN运行的Performance记录, 可以看到一次RN执行的几个主要环节. 获取js -> 执行js -> 初始化nativeModule -> 配置nativeModule. 从RCTPerformanceLogger.h
中可以看到几个环节的定义, 可以通过跟踪这几个定义看到整个过程.
1 获取js
第一次加载时(和修改js后), js文件会在node server上编译完成, 编译的过程主要是使用babel interpreter生成一个JSC能够执行的文件. 获取的过程就是通过外部传入的url请求获取js文件. 实现在RCTJavaScriptLoader
文件中, 简化后的代码如下, 支持从本地和http服务两种方式获取js.
2 执行js
这部分是RN的核心流程. 介绍执行之前需要了解下ios是如何调用js的. iOS7以后, Apple推出了JavaScriptCore framework, 封装了WebKit的js引擎, 提供了iOS上执行js的能力. JSC上有几个关键概念.
- JSVirtualMachine
- JSContext
- JSValue
JSVirtualMachine
封装了js的虚拟机, 处理内存管理和gc. 如果需要支持多线程的js执行, 可以创建多个JSVirtualMachine
.
JSContext
是javascript的运行环境, 用来执行js和保存变量, 相对于web开发中的window对象. 一个JSVirtualMachine
可以对应多个JSContext
, 不同的JSContext
间可以通信, 但JSVirtualMachine
之间是完全隔离的.
JSValue
是JSContext
中的对象的封装. JSContext
中可以创建任意个JSValue
.
详细的JSC使用知识超越了本文的范围, 想了解的朋友可以参考其他的资料OC, swift
好了, 看看怎么做的执行部分. 概括来讲, 首先会执行前面拿到的js, 执行完会发出一个RCTJavaScriptDidLoadNotification
消息. 接着会调用js的@"AppRegistry.runApplication"
方法, 这个方法会返回一个结果给到native, native根据这个结果做渲染.下面详细看看各个过程.
1 执行js 调用栈如下
| RCTContextExecutor
executeApplicationScript:sourceURL:onComplete:
执行js代码
| – | enqueueApplicationScript:url:onComplete
| – | – | RCTBatchedBridge
executeSourceCode:
Note:如果断点包含的RCTProfileBlock
没生效, 可以将RCTProfileBlock去掉, 直接调用block.
RN的js的执行是在RCTContextExecutor
中做的, 方法如下. RN中执行js的操作都是在一个独立的javascript线程中处理的. 后面也可以看到, RN中有3个主要的线程.
- com.facebook.React.JavaScript js代码运行的线程
- com.apple.main-thread 主线程, 所有UIKit相关处理
- com.facebook.React.ShadowQueue 布局相关处理
因此在监控性能的时候, RN中除了主线程的fps, 还可以看到JavaScript线程的fps, 这两部分都会影响整体的性能情况.
2 执行完成后通知js app启动
通过追踪RCTJavaScriptDidLoadNotification
消息, 可以找到第一次执行完js后的工作. 调用栈大致如下, 省略了中间几个方法.
| RCTBatchedBridge
executeSourceCode:
发送完成消息
|… |
| – | – | RCTBatchedBridge
- (void)enqueueJSCall:args:
执行@"AppRegistry.runApplication"
方法
[bridge enqueueJSCall:@"AppRegistry.runApplication" args:@[moduleName, appParameters]];
就是调用了AppRegistry.js的runApplication方法, 并带了模块名和参数. 可以通过Chrome的调试工具追踪js调用过程.
3 返回结果的格式
在 _executeJSCall的 @"AppRegistry.runApplication"
方法时断点可以获取返回值
External links
- Tadeu Zagallo, FB RN团队的开发分享的原理 http://tadeuzagallo.com/blog/react-native-bridge/
- RN社区翻译的中文文档 http://reactnative.cn/docs/getting-started.html
- 为什么Browser的响应速度比native慢 http://stackoverflow.com/questions/10731934/why-html-web-ui-response-slower-than-native-ui